Dépôt officiel du code source de l'ERP OpenConcerto
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationHistoryPanel.java |
---|
72,6 → 72,7 |
public void propertyChange(PropertyChangeEvent evt) { |
final List<SQLRowValues> selectedRows = list.getSelectedRows(); |
final IListPanel listePanel = listHistoriquePanel.getListePanel(0); |
// Activation/Desactivation des boutons Mofifier/Supprimer |
if (selectedRows != null && !selectedRows.isEmpty()) { |
final Set<Long> idsCalendarItemGroup = new HashSet<>(); |
for (SQLRowValues sqlRowValues : selectedRows) { |
142,24 → 143,25 |
cal.set(Calendar.YEAR, selectedYear + 1); |
Date dEnd = cal.getTime(); |
final SQLTable groupT = comboRequest.getPrimaryTable().getTable("CALENDAR_ITEM_GROUP"); |
final SQLTable calItemT = comboRequest.getPrimaryTable().getTable("CALENDAR_ITEM"); |
final SQLTable itemGroupTable = comboRequest.getPrimaryTable().getTable("CALENDAR_ITEM_GROUP"); |
final SQLTable itemTable = comboRequest.getPrimaryTable().getTable("CALENDAR_ITEM"); |
final SQLTable operationTable = comboRequest.getPrimaryTable().getTable("OPERATION"); |
final List<?> dateGroupIDs; |
{ |
final SQLSelect copy = new SQLSelect(input); |
copy.clearSelect(); |
copy.addSelect(copy.getAlias(groupT.getKey())); |
copy.setWhere(copy.getAlias(comboRequest.getPrimaryTable().getTable("OPERATION").getField("ID_SITE")), "=", panel.getSelectedRow().getID()); |
final List<?> allGroupIDs = calItemT.getDBSystemRoot().getDataSource().executeCol(copy.asString()); |
copy.addSelect(copy.getAlias(itemGroupTable.getKey())); |
copy.setWhere(copy.getAlias(operationTable.getField("ID_SITE")), "=", panel.getSelectedRow().getID()); |
final List<?> allGroupIDs = itemTable.getDBSystemRoot().getDataSource().executeCol(copy.asString()); |
final SQLSelect selIDGroup = new SQLSelect(); |
selIDGroup.addSelect(calItemT.getField("ID_CALENDAR_ITEM_GROUP")); |
final Where where = new Where(calItemT.getField("START"), dStart, true, dEnd, true); |
selIDGroup.setWhere(where).andWhere(new Where(calItemT.getField("ID_CALENDAR_ITEM_GROUP"), allGroupIDs)); |
dateGroupIDs = calItemT.getDBSystemRoot().getDataSource().executeCol(selIDGroup.asString()); |
selIDGroup.addSelect(itemTable.getField("ID_CALENDAR_ITEM_GROUP")); |
final Where where = new Where(itemTable.getField("START"), dStart, true, dEnd, true); |
selIDGroup.setWhere(where).andWhere(new Where(itemTable.getField("ID_CALENDAR_ITEM_GROUP"), allGroupIDs)); |
dateGroupIDs = itemTable.getDBSystemRoot().getDataSource().executeCol(selIDGroup.asString()); |
} |
input.setWhere(new Where(input.getAlias(groupT.getKey()), dateGroupIDs)); |
Where w = new Where(input.getAlias(itemGroupTable.getKey()), dateGroupIDs); |
input.setWhere(w); |
} catch (Throwable e) { |
e.printStackTrace(); |
} |
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/ModuleOperation.java |
---|
62,8 → 62,6 |
// SQLRequestLog.showFrame(); |
TemplateManager.getInstance().register(OPERATIONS_REPORT_TEMPLATE_ID); |
TemplateManager.getInstance().register(OPERATIONS_REPORT_TEMPLATE2_ID); |
// Translation loading |
TranslationManager.getInstance().addTranslationStreamFromClass(this.getClass()); |
} |
@Override |
148,6 → 146,10 |
createTableOperation.addVarCharColumn("DESCRIPTION", 10000); |
createTableOperation.addVarCharColumn("PLANNER_UID", 2048); |
createTableOperation.addVarCharColumn("PLANNER_XML", 2048); |
ctxt.executeSQL(); |
// SQLTable.setUndefID(ctxt.getRoot().getSchema(), TABLE_SITE, null); |
// SQLTable.setUndefID(ctxt.getRoot().getSchema(), TABLE_OPERATION, null); |
} |
} |
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/CalendarPrintPanel.java |
---|
43,6 → 43,8 |
private static final double POINTS_PER_INCH = 72.0; |
public CalendarPrintPanel(final OperationCalendarManager manager, final int week, final int year, final List<User> selectedUsers, final List<String> selectedStates) { |
System.err.println("CalendarPrintPanel.CalendarPrintPanel()" + selectedUsers); |
preview.setSelected(true); |
// |
this.setLayout(new GridBagLayout()); |
109,8 → 111,15 |
}); |
final PageFormat pf = new PageFormat(); |
pf.setPaper(new A4()); |
final CalendarItemPrinter printable = new OperationCalendarItemPrinter(user.getFullName(), itemInWeek, pf); |
List<JCalendarItem> itemsToWork = new ArrayList<>(); |
for (JCalendarItem item : itemInWeek) { |
if (!item.hasFlag(ModuleOperation.FREE_TIME_FLAG)) { |
itemsToWork.add(item); |
} |
} |
final CalendarItemPrinter printable = new OperationCalendarItemPrinter(user.getFullName(), itemInWeek, pf, itemsToWork); |
p.add(printable); |
} |
} |
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationCalendarPanel.java |
---|
117,12 → 117,11 |
this.beginStateSaving(conf.getConfDir(), w); |
} |
public static Map<Integer, Long> getDurations(List<List<JCalendarItem>> list, final Flag requiredFlag) { |
public static Map<Integer, Long> getDurations(List<List<JCalendarItem>> list, final Flag requiredFlag, final Flag excludedFlag) { |
final Map<Integer, Long> res = new HashMap<>(); |
final Flag freeTimeFlag = ModuleOperation.FREE_TIME_FLAG; |
for (List<JCalendarItem> items : list) { |
for (JCalendarItem item : items) { |
if (!item.hasFlag(freeTimeFlag) && (requiredFlag == null || item.hasFlag(requiredFlag)) && item.getCookie() instanceof SQLRowValues) { |
if (!item.hasFlag(excludedFlag) && (requiredFlag == null || item.hasFlag(requiredFlag)) && item.getCookie() instanceof SQLRowValues) { |
final SQLRowValues user = (SQLRowValues) item.getCookie(); |
final long toAddMinutes = (item.getDtEnd().getTimeInMillis() - item.getDtStart().getTimeInMillis()) / (60 * 1000); |
final Integer key = user.getID(); |
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/UserOperationListModel.java |
---|
140,7 → 140,7 |
return this.usersAndWeeklyMinutes; |
} |
} |
final Map<User, Integer> uInfo = new LinkedHashMap<User, Integer>(); |
final Map<User, Integer> uInfo = new LinkedHashMap<>(); |
final SQLRowValues v = new SQLRowValues(this.salarieElem.getTable()); |
v.putNulls("NOM", "PRENOM"); |
v.putRowValues("ID_INFOS_SALARIE_PAYE").putNulls("DUREE_HEBDO"); |
152,10 → 152,14 |
for (int i = 0; i < size; i++) { |
final User u = users.get(i); |
final String name = u.getName().trim(); |
final String firstName = u.getFirstName(); |
final String firstName = u.getFirstName().trim(); |
Integer minutes = null; |
for (SQLRowValues row : rows) { |
if (row.getString("NOM").trim().equalsIgnoreCase(name) && row.getString("PRENOM").trim().equalsIgnoreCase(firstName)) { |
// Matching Utilisateur <-> Salarié |
// Nom et prénom identique |
final String sName = row.getString("NOM").trim(); |
final String sFirstName = row.getString("PRENOM").trim(); |
if (sName.equalsIgnoreCase(name) && sFirstName.equalsIgnoreCase(firstName)) { |
minutes = (int) row.getForeign("ID_INFOS_SALARIE_PAYE").getFloat("DUREE_HEBDO") * 60; |
break; |
} |
169,8 → 173,8 |
} |
private void setDurations(final List<List<JCalendarItem>> viewItems) { |
final Map<Integer, Long> all = OperationCalendarPanel.getDurations(viewItems, null); |
final Map<Integer, Long> locked = OperationCalendarPanel.getDurations(viewItems, Flag.getFlag("locked")); |
final Map<Integer, Long> all = OperationCalendarPanel.getDurations(viewItems, null, ModuleOperation.FREE_TIME_FLAG); |
final Map<Integer, Long> locked = OperationCalendarPanel.getDurations(viewItems, Flag.getFlag("locked"), ModuleOperation.FREE_TIME_FLAG); |
synchronized (this) { |
this.allDurations = Collections.unmodifiableMap(all); |
this.lockedDurations = Collections.unmodifiableMap(locked); |
217,10 → 221,10 |
// not a SALARIE |
suffix = ""; |
} else { |
// Durée verrouillée |
final int d2 = getDuration(locked, u.getId()); |
// Durée planifiée |
final int d = getDuration(all, u.getId()); |
// Durée verrouillée |
final int d2 = getDuration(locked, u.getId()); |
suffix = " [" + formatDuration(d2) + " / " + formatDuration(d) + " / " + formatDuration(weeklyMinutes) + "]"; |
} |
res.add(createItem(u, (u.getFullName() + suffix).trim())); |
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/UserColor.java |
---|
18,12 → 18,16 |
final int size = users.size(); |
for (int i = 0; i < size; i++) { |
final User u = users.get(i); |
map.put(u.getId(), Color.decode(COLORS[i % COLORS.length])); |
if (u.getColor() == null) { |
this.map.put(u.getId(), Color.decode(COLORS[i % COLORS.length])); |
} else { |
this.map.put(u.getId(), u.getColor()); |
} |
} |
} |
public synchronized Color getColor(int id) { |
return map.get(id); |
return this.map.get(id); |
} |
public static final synchronized UserColor getInstance() { |
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationExportPanel.java |
---|
74,13 → 74,13 |
public class OperationExportPanel extends JPanel { |
@GuardedBy("EDT") |
static private final DateFormat DF = new SimpleDateFormat("yyyyMMdd"); |
private static final DateFormat DF = new SimpleDateFormat("yyyyMMdd"); |
final JCheckBox lockedCheckBox = new JCheckBox("verrouillées uniquement"); |
final JButton bPrint = new JButton("Exporter"); |
public OperationExportPanel(final OperationCalendarManager manager, final List<SQLRowValues> rowsSite) { |
lockedCheckBox.setSelected(true); |
this.lockedCheckBox.setSelected(true); |
// |
this.setLayout(new GridBagLayout()); |
final GridBagConstraints c = new DefaultGridBagConstraints(); |
141,8 → 141,8 |
// |
final JPanel p = new JPanel(); |
p.setLayout(new FlowLayout(FlowLayout.RIGHT)); |
p.add(lockedCheckBox); |
p.add(bPrint); |
p.add(this.lockedCheckBox); |
p.add(this.bPrint); |
c.gridwidth = 2; |
c.gridx = 0; |
c.gridy++; |
151,7 → 151,7 |
this.add(p, c); |
// |
bPrint.addActionListener(new ActionListener() { |
this.bPrint.addActionListener(new ActionListener() { |
@Override |
public void actionPerformed(ActionEvent e) { |
160,9 → 160,9 |
} |
final String statusVal = statusCombo.getValue(); |
final List<String> states = StringUtils.isEmpty(statusVal, true) ? null : Collections.singletonList(statusVal); |
final List<JCalendarItem> items = manager.getItemIn(d1.getDate(), d2.getDate(), null, states); |
final List<JCalendarItemDB> itemsToExport = new ArrayList<JCalendarItemDB>(items.size()); |
if (lockedCheckBox.isSelected()) { |
final List<JCalendarItem> items = manager.getItemIn(d1.getDate(), d2.getDate(), manager.getAllUsers(), states); |
final List<JCalendarItemDB> itemsToExport = new ArrayList<>(items.size()); |
if (OperationExportPanel.this.lockedCheckBox.isSelected()) { |
for (JCalendarItem jCalendarItem : items) { |
JCalendarItemDB i = (JCalendarItemDB) jCalendarItem; |
if (i.getFlagsString().contains("locked")) { |
176,12 → 176,12 |
} |
} |
if (rowsSite != null && !rowsSite.isEmpty()) { |
final Set<String> allowedSites = new HashSet<String>(); |
final Set<String> allowedSites = new HashSet<>(); |
for (SQLRowValues r : rowsSite) { |
String siteName = r.getString("NAME"); |
allowedSites.add(siteName); |
} |
final List<JCalendarItemDB> filtered = new ArrayList<JCalendarItemDB>(itemsToExport.size()); |
final List<JCalendarItemDB> filtered = new ArrayList<>(itemsToExport.size()); |
for (JCalendarItemDB i : itemsToExport) { |
if (allowedSites.contains(i.getSiteName())) { |
filtered.add(i); |
218,9 → 218,9 |
}); |
} |
static private final class Planner implements Comparable<Planner> { |
private static final class Planner implements Comparable<Planner> { |
static private final BigDecimal MS_PER_HOUR = BigDecimal.valueOf(1000 * 3600); |
private static final BigDecimal MS_PER_HOUR = BigDecimal.valueOf(1000 * 3600); |
private final String uid; |
private final String xml; |
256,7 → 256,7 |
final Element scheduleElem = doc.getRootElement().getChild("schedule"); |
this.startTime = new Date(Long.valueOf(scheduleElem.getAttributeValue("start"))); |
final long endTime = Long.valueOf(scheduleElem.getAttributeValue("end")); |
final long endTime = Long.parseLong(scheduleElem.getAttributeValue("end")); |
this.hours = DecimalUtils.round(BigDecimal.valueOf(endTime - this.startTime.getTime()).divide(MS_PER_HOUR, DecimalUtils.HIGH_PRECISION), 5); |
} catch (Exception e) { |
throw new IllegalStateException("couldn't get start for " + this.xml, e); |
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationCalendarManager.java |
---|
62,6 → 62,10 |
this.userMngr = userMngr; |
} |
public UserManager getUserMngr() { |
return userMngr; |
} |
public final SQLElementDirectory getDirectory() { |
return this.dir; |
} |
389,4 → 393,15 |
} |
return l.get(0); |
} |
/** |
* Enabled or disabled users |
*/ |
public List<User> getAllUsers() { |
final List<User> result = new ArrayList<>(); |
for (User user : this.userMngr.getUsers().values()) { |
result.add(user); |
} |
return result; |
} |
} |
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/JCalendarItemDB.java |
---|
25,7 → 25,13 |
assert r.isFrozen(); |
this.item = r; |
this.group = dir.getElement(this.item.getTable()).getContainer(this.item); |
if (this.group == null) { |
throw new IllegalArgumentException("no group found for row " + r); |
} |
this.operationElem = dir.getElement(OperationSQLElement.class); |
if (this.operationElem == null) { |
throw new IllegalStateException("no element found " + OperationSQLElement.class); |
} |
this.operation = CollectionUtils.getSole(this.group.getReferentRows(this.operationElem.getTable())); |
} |
84,7 → 90,7 |
} |
public String getStatus() { |
return status; |
return this.status; |
} |
public void setOperationType(String type) { |
92,7 → 98,7 |
} |
public String getType() { |
return type; |
return this.type; |
} |
public String getFlagsString() { |
109,7 → 115,7 |
} |
public String getPlannerXML() { |
return plannerXML; |
return this.plannerXML; |
} |
public void setPlannerXML(String string) { |
117,7 → 123,7 |
} |
public String getPlannerUID() { |
return plannerUID; |
return this.plannerUID; |
} |
public void setPlannerUID(String plannerUID) { |
125,7 → 131,7 |
} |
public String getSiteName() { |
return siteName; |
return this.siteName; |
} |
public void setSiteName(String siteName) { |
133,7 → 139,7 |
} |
public String getSiteComment() { |
return siteComment; |
return this.siteComment; |
} |
public void setSiteComment(String siteComment) { |
145,7 → 151,7 |
} |
public Number getSiteId() { |
return siteId; |
return this.siteId; |
} |
public int getId() { |
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationCalendarItemPrinter.java |
---|
11,9 → 11,11 |
public class OperationCalendarItemPrinter extends CalendarItemPrinter { |
public static final Font FONT_LINE = new Font("Arial", Font.PLAIN, 9); |
private List<JCalendarItem> itemsToWork; |
public OperationCalendarItemPrinter(String title, List<JCalendarItem> items, PageFormat pf) { |
public OperationCalendarItemPrinter(String title, List<JCalendarItem> items, PageFormat pf, List<JCalendarItem> itemsToWork) { |
super(title, items, pf); |
this.itemsToWork = itemsToWork; |
} |
@Override |
38,7 → 40,7 |
@Override |
public String getTitle() { |
final List<JCalendarItem> items = this.getItems(); |
final List<JCalendarItem> items = this.itemsToWork; |
int totalMinutes = 0; |
for (JCalendarItem jCalendarItem : items) { |
long t2 = jCalendarItem.getDtEnd().getTimeInMillis(); |
/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/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 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 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 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 Label/lib/barcode4j-2.1.0.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/Modules/Module Label/lib/barcode4j-2.1.0.jar |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/Modules/Module Label/lib/jbarcode-0.2.8.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/Modules/Module Label/lib/jbarcode-0.2.8.jar |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/oc-qrcode.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/oc-qrcode.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/Label.java |
---|
New file |
0,0 → 1,5 |
package org.openconcerto.modules.label; |
public class Label { |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/default-zpl.txt |
---|
New file |
0,0 → 1,20 |
^XA |
^FX Titre |
^CF0,42^FO150,43^FB750,1,0,L^FDXXXXXXXXXX^FS |
^FX DataMatrix |
^FO1010,20^BXN,7,200,,,,@^FDOPENCONCERTO^FS |
^FX Nom du produit |
^CF0,40 |
^FO150,140^FB700,1,0,L^FDYYYYYYYYYY^FS |
^FX Code barre |
^FO150,220 |
^BY3,2,300 |
^BCN,300,Y,N,Y,D |
^FD(02)7612345678900(15)201218(37)9999^FS |
^FO150,620^BY3,2,300^BCN,300,Y,N,Y,D^FD(10)LLLLLLLLL^FS |
^XZ |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/ModuleLabel.java |
---|
4,10 → 4,17 |
import java.awt.Graphics; |
import java.awt.event.ActionEvent; |
import java.awt.geom.Rectangle2D; |
import java.io.File; |
import java.io.IOException; |
import java.nio.charset.StandardCharsets; |
import java.util.ArrayList; |
import java.util.LinkedHashMap; |
import java.util.List; |
import java.util.Map.Entry; |
import java.util.TreeMap; |
import javax.swing.AbstractAction; |
import javax.swing.JFrame; |
import javax.swing.SwingWorker; |
import org.openconcerto.erp.generationDoc.provider.AdresseFullClientValueProvider; |
14,6 → 21,7 |
import org.openconcerto.erp.modules.AbstractModule; |
import org.openconcerto.erp.modules.ComponentsContext; |
import org.openconcerto.erp.modules.ModuleFactory; |
import org.openconcerto.sql.model.SQLRow; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.sql.model.SQLRowValues; |
import org.openconcerto.sql.model.SQLRowValuesListFetcher; |
23,10 → 31,12 |
import org.openconcerto.sql.view.list.IListeAction.IListeEvent; |
import org.openconcerto.sql.view.list.RowAction.PredicateRowAction; |
import org.openconcerto.utils.ExceptionHandler; |
import org.openconcerto.utils.FileUtils; |
import org.openconcerto.utils.GestionDevise; |
import org.openconcerto.utils.StringUtils; |
public final class ModuleLabel extends AbstractModule { |
final LinkedHashMap<String, String> zplTemplates = new LinkedHashMap<String, String>(); |
public ModuleLabel(ModuleFactory f) throws IOException { |
super(f); |
34,6 → 44,9 |
@Override |
protected void setupComponents(ComponentsContext ctxt) { |
readTemplates(new File("Template/Labels")); |
readTemplates(new File("Configuration/Template/Labels")); |
final String actionName = "Imprimer les étiquettes"; |
final PredicateRowAction aArticle = new PredicateRowAction(new AbstractAction(actionName) { |
41,26 → 54,31 |
public void actionPerformed(ActionEvent arg0) { |
final IListe list = IListe.get(arg0); |
final List<Integer> selectedIDs = list.getSelection().getSelectedIDs(); |
final SwingWorker<List<SQLRowValues>, String> wworker = new SwingWorker<List<SQLRowValues>, String>() { |
final SQLTable tArticle = list.getSelectedRows().get(0).getTable(); |
final SwingWorker<List<RowValuesLabel>, String> wworker = new SwingWorker<List<RowValuesLabel>, String>() { |
@Override |
protected List<SQLRowValues> doInBackground() throws Exception { |
final SQLTable tArticle = list.getSelectedRows().get(0).getTable(); |
protected List<RowValuesLabel> doInBackground() throws Exception { |
final SQLRowValues graph = new SQLRowValues(tArticle); |
graph.putNulls("NOM", "PV_TTC"); |
final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(graph); |
final List<SQLRowValues> values = fetcher.fetch(new Where(tArticle.getKey(), selectedIDs)); |
return values; |
final List<SQLRowValues> rows = fetcher.fetch(new Where(tArticle.getKey(), selectedIDs)); |
final List<RowValuesLabel> list = new ArrayList<>(rows.size()); |
for (SQLRowValues row : rows) { |
list.add(new RowValuesLabel(row)); |
} |
return list; |
} |
@Override |
protected void done() { |
try { |
final List<SQLRowValues> values = get(); |
final List<RowValuesLabel> values = get(); |
final LabelFrame f = new LabelFrame(values, new LabelRenderer() { |
@Override |
public void paintLabel(Graphics g, SQLRowAccessor row, int x, int y, int gridWith, int gridHeight, float fontSize) { |
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize) { |
g.setColor(Color.BLACK); |
g.setFont(g.getFont().deriveFont(fontSize)); |
// Labels borders |
67,6 → 85,7 |
final int hBorder = 12; |
final int vBorder = 8; |
// Product name |
SQLRowValues row = ((RowValuesLabel) label).getSQLRowValues(); |
final String text = row.getString("NOM"); |
final List<String> l = StringUtils.wrap(text, g.getFontMetrics(), gridWith - 2 * hBorder); |
final int lineHeight = g.getFontMetrics().getHeight(); |
73,7 → 92,7 |
int lineY = y; |
final int margin = gridHeight - l.size() * lineHeight; |
if (margin > 0) { |
lineY += (int) (margin / 2); |
lineY += margin / 2; |
} |
for (String line : l) { |
g.drawString(line, x + hBorder, lineY); |
95,8 → 114,8 |
} catch (Exception e) { |
ExceptionHandler.handle("Erreur d'impression", e); |
} |
} |
}; |
}; |
wworker.execute(); |
} |
107,11 → 126,11 |
public void actionPerformed(ActionEvent arg0) { |
final IListe list = IListe.get(arg0); |
final List<Integer> selectedIDs = list.getSelection().getSelectedIDs(); |
final SwingWorker<List<SQLRowValues>, String> wworker = new SwingWorker<List<SQLRowValues>, String>() { |
final SQLTable tClient = list.getSelectedRows().get(0).getTable(); |
final SwingWorker<List<RowValuesLabel>, String> wworker = new SwingWorker<List<RowValuesLabel>, String>() { |
@Override |
protected List<SQLRowValues> doInBackground() throws Exception { |
final SQLTable tClient = list.getSelectedRows().get(0).getTable(); |
protected List<RowValuesLabel> doInBackground() throws Exception { |
final SQLRowValues graph = new SQLRowValues(tClient); |
graph.putNulls("NOM"); |
final SQLRowValues a1 = graph.putRowValues("ID_ADRESSE"); |
119,17 → 138,24 |
final SQLRowValues a2 = graph.putRowValues("ID_ADRESSE_L"); |
a2.putNulls(a2.getTable().getFieldsName()); |
final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(graph); |
final List<SQLRowValues> values = fetcher.fetch(new Where(tClient.getKey(), selectedIDs)); |
return values; |
final List<SQLRowValues> rows = fetcher.fetch(new Where(tClient.getKey(), selectedIDs)); |
final List<RowValuesLabel> list = new ArrayList<>(rows.size()); |
for (SQLRowValues row : rows) { |
list.add(new RowValuesLabel(row)); |
} |
return list; |
} |
@Override |
protected void done() { |
try { |
final List<SQLRowValues> values = get(); |
final List<RowValuesLabel> values = get(); |
final LabelFrame f = new LabelFrame(values, new LabelRenderer() { |
@Override |
public void paintLabel(Graphics g, SQLRowAccessor row, int x, int y, int gridWith, int gridHeight, float fontSize) { |
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize) { |
SQLRowValues row = ((RowValuesLabel) label).getSQLRowValues(); |
SQLRowAccessor rAddr = row.getForeign("ID_ADRESSE_L"); |
if (rAddr == null || rAddr.isUndefined()) { |
rAddr = row.getForeign("ID_ADRESSE"); |
165,8 → 191,8 |
} catch (Exception e) { |
ExceptionHandler.handle("Erreur d'impression", e); |
} |
} |
}; |
}; |
wworker.execute(); |
} |
176,13 → 202,94 |
aClient.setPredicate(IListeEvent.createSelectionCountPredicate(1, Integer.MAX_VALUE)); |
ctxt.getElement("ARTICLE").getRowActions().add(aArticle); |
ctxt.getElement("CLIENT").getRowActions().add(aClient); |
if (!this.zplTemplates.isEmpty()) { |
for (final Entry<String, String> entry : this.zplTemplates.entrySet()) { |
final String zpl = entry.getValue(); |
final PredicateRowAction action = new PredicateRowAction(new AbstractAction("Imprimer l'étiquette " + entry.getKey()) { |
@Override |
public void actionPerformed(ActionEvent arg0) { |
final ZPLPrinterPanel p = new ZPLPrinterPanel(zpl); |
final JFrame f = new JFrame(); |
final IListe list = IListe.get(arg0); |
final int idProduct = list.getSelection().getSelectedID(); |
final SQLTable tArticle = list.getSelectedRows().get(0).getTable(); |
final SwingWorker<SQLRowValues, String> wworker = new SwingWorker<SQLRowValues, String>() { |
@Override |
protected SQLRowValues doInBackground() throws Exception { |
final SQLRow row = tArticle.getRow(idProduct); |
row.fetchValues(); |
return row.asRowValues(); |
} |
@Override |
protected void done() { |
try { |
final SQLRowValues values = get(); |
p.initUI(values); |
f.setTitle(entry.getKey()); |
f.setContentPane(p); |
f.pack(); |
f.setLocationRelativeTo(null); |
f.setVisible(true); |
} catch (Exception e) { |
ExceptionHandler.handle("Erreur d'impression", e); |
} |
} |
}; |
wworker.execute(); |
} |
}, true, false); |
action.setPredicate(IListeEvent.createSelectionCountPredicate(1, 1)); |
ctxt.getElement("ARTICLE").getRowActions().add(action); |
} |
} |
} |
@Override |
protected void start() { |
} |
private void readTemplates(File templatesDir) { |
System.out.println("ModuleLabel.readTemplates() " + templatesDir.getAbsolutePath()); |
if (templatesDir.exists() && templatesDir.isDirectory()) { |
System.err.println("ModuleLabel.readTemplates() " + templatesDir.getAbsolutePath()); |
File[] files = templatesDir.listFiles(); |
if (files != null) { |
LinkedHashMap<String, String> map = new LinkedHashMap<>(); |
for (File f : files) { |
if (f.getName().endsWith(".zpl")) { |
try { |
String zpl = FileUtils.read(f, StandardCharsets.UTF_8); |
String name = f.getName().substring(0, f.getName().length() - 4).trim(); |
map.put(name, zpl); |
System.err.println("ModuleLabel.readTemplates() add " + name); |
} catch (Exception e) { |
System.err.println(this.getClass().getCanonicalName() + "start() cannot read zpl template : " + f.getAbsolutePath() + " : " + e.getMessage()); |
} |
} |
} |
// Tri de la map par clef |
final TreeMap<String, String> copy = new TreeMap<>(map); |
this.zplTemplates.clear(); |
this.zplTemplates.putAll(copy); |
} |
} else { |
System.err.println("ModuleLabel.readTemplates() " + templatesDir.getAbsolutePath() + " missing"); |
} |
} |
@Override |
protected void stop() { |
// nothing |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/RowValuesLabel.java |
---|
New file |
0,0 → 1,16 |
package org.openconcerto.modules.label; |
import org.openconcerto.sql.model.SQLRowValues; |
public class RowValuesLabel extends Label { |
private SQLRowValues row; |
public RowValuesLabel(SQLRowValues row) { |
this.row = row; |
} |
public SQLRowValues getSQLRowValues() { |
return row; |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/32.png |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/32.png |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/ZPLPrinterPanel.java |
---|
New file |
0,0 → 1,409 |
package org.openconcerto.modules.label; |
import java.awt.FlowLayout; |
import java.awt.GridBagConstraints; |
import java.awt.GridBagLayout; |
import java.awt.event.ActionEvent; |
import java.awt.event.ActionListener; |
import java.awt.print.PrinterJob; |
import java.io.BufferedReader; |
import java.io.DataOutputStream; |
import java.io.File; |
import java.io.FileInputStream; |
import java.io.FileOutputStream; |
import java.io.IOException; |
import java.io.StringReader; |
import java.net.Socket; |
import java.nio.charset.StandardCharsets; |
import java.text.DecimalFormat; |
import java.util.ArrayList; |
import java.util.HashMap; |
import java.util.HashSet; |
import java.util.List; |
import java.util.Map; |
import java.util.Properties; |
import java.util.Set; |
import javax.print.DocFlavor; |
import javax.print.DocPrintJob; |
import javax.print.PrintService; |
import javax.print.SimpleDoc; |
import javax.swing.ButtonGroup; |
import javax.swing.JButton; |
import javax.swing.JFrame; |
import javax.swing.JLabel; |
import javax.swing.JOptionPane; |
import javax.swing.JPanel; |
import javax.swing.JRadioButton; |
import javax.swing.JSpinner; |
import javax.swing.JTextField; |
import javax.swing.SpinnerNumberModel; |
import javax.swing.SwingConstants; |
import javax.swing.SwingUtilities; |
import javax.swing.UIManager; |
import javax.swing.UnsupportedLookAndFeelException; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.ui.DefaultGridBagConstraints; |
import org.openconcerto.ui.JLabelBold; |
import org.openconcerto.utils.BaseDirs; |
import org.openconcerto.utils.ExceptionHandler; |
import org.openconcerto.utils.FileUtils; |
import org.openconcerto.utils.ProductInfo; |
public class ZPLPrinterPanel extends JPanel { |
private final HashMap<String, String> mapName = new HashMap<>(); |
private final List<String> variables; |
private final List<String> knownVariables = new ArrayList<>(); |
private Map<String, JTextField> editorMap = new HashMap<>(); |
private String zpl; |
private final Properties properties = new Properties(); |
public static void main(String[] args) throws IOException { |
// final File f = new File("Templates/Labels", "50x50.zpl"); |
final File file = new File("Template/Labels", "57x32.zpl"); |
String zpl = FileUtils.read(file); |
SwingUtilities.invokeLater(new Runnable() { |
@Override |
public void run() { |
try { |
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); |
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { |
e.printStackTrace(); |
} |
ZPLPrinterPanel p = new ZPLPrinterPanel(zpl); |
p.initUI(null); |
JFrame f = new JFrame(); |
f.setTitle(file.getName()); |
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
f.setContentPane(p); |
f.pack(); |
f.setLocationRelativeTo(null); |
f.setVisible(true); |
} |
}); |
} |
public ZPLPrinterPanel(String zpl) { |
this.zpl = zpl; |
this.variables = getVariables(zpl); |
knownVariables.add("product.code"); |
knownVariables.add("product.name"); |
knownVariables.add("product.material"); |
knownVariables.add("product.ean13"); |
knownVariables.add("product.price"); |
knownVariables.add("product.pricewithtax"); |
// |
mapName.put("product.name", "Nom"); |
mapName.put("product.code", "Code"); |
mapName.put("product.ean13", "Code à barres"); |
mapName.put("product.price", "Prix HT"); |
mapName.put("product.pricewithtax", "Prix TTC"); |
mapName.put("product.treatment", "Traitement"); |
mapName.put("product.origin", "Origine"); |
mapName.put("product.batch", "Lot"); |
mapName.put("product.size", "Taille"); |
mapName.put("product.color", "Couleur"); |
mapName.put("product.material", "Matière"); |
} |
private final File getPrefFile() { |
final File prefsFolder = BaseDirs.create(ProductInfo.getInstance()).getPreferencesFolder(); |
if (!prefsFolder.exists()) { |
prefsFolder.mkdirs(); |
} |
return new File(prefsFolder, "labels.properties"); |
} |
protected void initUI(SQLRowAccessor row) { |
final File prefsFolder = BaseDirs.create(ProductInfo.getInstance()).getPreferencesFolder(); |
if (!prefsFolder.exists()) { |
prefsFolder.mkdirs(); |
} |
final File file = getPrefFile(); |
System.out.println(file.getAbsolutePath()); |
if (file.exists()) { |
try { |
properties.load(new FileInputStream(file)); |
} catch (IOException e) { |
e.printStackTrace(); |
} |
} |
this.setLayout(new GridBagLayout()); |
GridBagConstraints c = new DefaultGridBagConstraints(); |
c.fill = GridBagConstraints.HORIZONTAL; |
this.removeAll(); |
// Fields |
Set<String> added = new HashSet<>(); |
for (String v : this.knownVariables) { |
if (variables.contains(v)) { |
// Non editable |
String label = getName(v); |
c.gridx = 0; |
c.weightx = 0; |
this.add(new JLabel(label, SwingConstants.RIGHT), c); |
c.gridx++; |
c.weightx = 1; |
String value = getValueAsString(row, v); |
JTextField txt = new JTextField(20); |
if (value != null) { |
txt.setText(value); |
txt.setEditable(false); |
} |
editorMap.put(v, txt); |
this.add(txt, c); |
added.add(v); |
c.gridy++; |
} |
} |
for (String v : this.variables) { |
if (!added.contains(v)) { |
// Editable |
String label = getName(v); |
c.gridx = 0; |
c.weightx = 0; |
this.add(new JLabel(label, SwingConstants.RIGHT), c); |
c.gridx++; |
c.weightx = 1; |
JTextField txt = new JTextField(20); |
editorMap.put(v, txt); |
this.add(txt, c); |
added.add(v); |
c.gridy++; |
} |
} |
c.gridwidth = 2; |
c.gridx = 0; |
this.add(new JLabelBold("Paramètres d'impression"), c); |
// Printer selector |
c.gridx = 0; |
c.gridy++; |
final JPanel l1 = new JPanel(); |
l1.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 0)); |
l1.add(new JLabel("Nombre d'étiquettes")); |
final JSpinner nbLabels = new JSpinner(new SpinnerNumberModel(1, 1, 1000, 10)); |
l1.add(nbLabels); |
this.add(l1, c); |
// Delay |
l1.add(new JLabel(" Pause entre chaque impression")); |
final JSpinner delayLabels = new JSpinner(new SpinnerNumberModel(800, 100, 10000, 100)); |
l1.add(delayLabels); |
this.add(l1, c); |
l1.add(new JLabel("ms")); |
c.gridy++; |
final JPanel lPrintNetwork = new JPanel(); |
lPrintNetwork.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 0)); |
JRadioButton radioNetworkPrinter = new JRadioButton("imprimante réseau IP :"); |
lPrintNetwork.add(radioNetworkPrinter); |
JTextField textPrinterIP = new JTextField(16); |
lPrintNetwork.add(textPrinterIP); |
lPrintNetwork.add(new JLabel(" Port :")); |
JSpinner portZPL = new JSpinner(new SpinnerNumberModel(9100, 24, 10000, 1)); |
lPrintNetwork.add(portZPL); |
this.add(lPrintNetwork, c); |
c.gridy++; |
final JPanel lPrintLocal = new JPanel(); |
lPrintLocal.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 0)); |
JRadioButton radioLocalPrinter = new JRadioButton("imprimante locale"); |
lPrintLocal.add(radioLocalPrinter); |
radioLocalPrinter.setSelected(true); |
this.add(lPrintLocal, c); |
final ButtonGroup gr = new ButtonGroup(); |
gr.add(radioLocalPrinter); |
gr.add(radioNetworkPrinter); |
c.gridy++; |
c.weighty = 1; |
c.gridx = 1; |
c.fill = GridBagConstraints.NONE; |
c.anchor = GridBagConstraints.SOUTHEAST; |
// Restore state from properties |
if (properties.getOrDefault("printerType", "local").equals("local")) { |
radioLocalPrinter.setSelected(true); |
} else { |
radioNetworkPrinter.setSelected(true); |
} |
textPrinterIP.setText(properties.getOrDefault("printerIp", "").toString()); |
portZPL.setValue(Long.parseLong(properties.getOrDefault("printerPort", "9100").toString())); |
nbLabels.setValue(Long.parseLong(properties.getOrDefault("nbLabels", "1").toString())); |
delayLabels.setValue(Long.parseLong(properties.getOrDefault("delay", "800").toString())); |
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/GS1AIElements.java |
---|
New file |
0,0 → 1,259 |
package org.openconcerto.modules.label.gs1; |
import java.io.PrintStream; |
import java.util.ArrayList; |
import java.util.HashMap; |
public class GS1AIElements { |
static final HashMap<String, GS1ApplicationIdentifier> GS1_128_AI = new HashMap<>(); |
static { |
GS1_128_AI.put("00", new GS1ApplicationIdentifier(0, 18, false, false)); |
GS1_128_AI.put("01", new GS1ApplicationIdentifier(0, 14, false, false)); |
GS1_128_AI.put("02", new GS1ApplicationIdentifier(0, 14, false, false)); |
GS1_128_AI.put("10", new GS1ApplicationIdentifier(0, 20, true, false)); |
GS1_128_AI.put("11", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("12", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("13", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("14", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("15", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("17", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("20", new GS1ApplicationIdentifier(0, 2, false, false)); |
GS1_128_AI.put("21", new GS1ApplicationIdentifier(0, 20, true, false)); |
GS1_128_AI.put("240", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("241", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("242", new GS1ApplicationIdentifier(0, 6, true, false)); |
GS1_128_AI.put("250", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("251", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("253", new GS1ApplicationIdentifier(13, 30, true, false)); |
GS1_128_AI.put("254", new GS1ApplicationIdentifier(0, 20, true, false)); |
GS1_128_AI.put("255", new GS1ApplicationIdentifier(13, 25, true, false)); |
GS1_128_AI.put("30", new GS1ApplicationIdentifier(0, 8, true, false)); |
GS1_128_AI.put("310", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("311", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("312", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("313", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("314", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("315", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("316", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("320", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("321", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("322", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("323", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("324", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("325", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("326", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("327", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("328", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("329", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("330", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("331", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("332", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("333", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("334", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("335", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("336", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("340", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("341", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("342", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("343", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("344", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("345", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("346", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("347", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("348", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("349", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("350", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("351", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("352", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("353", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("354", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("355", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("356", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("357", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("360", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("361", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("362", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("363", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("364", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("365", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("366", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("367", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("368", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("369", new GS1ApplicationIdentifier(0, 6, false, true)); |
GS1_128_AI.put("37", new GS1ApplicationIdentifier(0, 8, true, false)); |
GS1_128_AI.put("390", new GS1ApplicationIdentifier(0, 15, true, true)); |
GS1_128_AI.put("391", new GS1ApplicationIdentifier(3, 18, true, true)); |
GS1_128_AI.put("392", new GS1ApplicationIdentifier(0, 15, true, true)); |
GS1_128_AI.put("393", new GS1ApplicationIdentifier(3, 18, true, true)); |
GS1_128_AI.put("400", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("401", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("402", new GS1ApplicationIdentifier(0, 17, false, false)); |
GS1_128_AI.put("403", new GS1ApplicationIdentifier(3, 30, true, false)); |
GS1_128_AI.put("410", new GS1ApplicationIdentifier(0, 17, false, false)); |
GS1_128_AI.put("411", new GS1ApplicationIdentifier(0, 17, false, false)); |
GS1_128_AI.put("412", new GS1ApplicationIdentifier(0, 17, false, false)); |
GS1_128_AI.put("413", new GS1ApplicationIdentifier(0, 17, false, false)); |
GS1_128_AI.put("414", new GS1ApplicationIdentifier(0, 17, false, false)); |
GS1_128_AI.put("420", new GS1ApplicationIdentifier(0, 20, true, false)); |
GS1_128_AI.put("421", new GS1ApplicationIdentifier(0, 0, true, false)); |
GS1_128_AI.put("422", new GS1ApplicationIdentifier(0, 3, false, false)); |
GS1_128_AI.put("423", new GS1ApplicationIdentifier(3, 15, true, false)); |
GS1_128_AI.put("424", new GS1ApplicationIdentifier(0, 3, false, false)); |
GS1_128_AI.put("425", new GS1ApplicationIdentifier(0, 3, false, false)); |
GS1_128_AI.put("426", new GS1ApplicationIdentifier(0, 3, false, false)); |
GS1_128_AI.put("7001", new GS1ApplicationIdentifier(0, 13, false, false)); |
GS1_128_AI.put("7002", new GS1ApplicationIdentifier(0, 30, false, false)); |
GS1_128_AI.put("7003", new GS1ApplicationIdentifier(0, 10, false, false)); |
GS1_128_AI.put("7004", new GS1ApplicationIdentifier(0, 4, true, false)); |
GS1_128_AI.put("8001", new GS1ApplicationIdentifier(0, 14, false, false)); |
GS1_128_AI.put("8002", new GS1ApplicationIdentifier(0, 20, true, false)); |
GS1_128_AI.put("8003", new GS1ApplicationIdentifier(14, 30, true, false)); |
GS1_128_AI.put("8004", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("8005", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("8006", new GS1ApplicationIdentifier(0, 18, false, false)); |
GS1_128_AI.put("8007", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("8008", new GS1ApplicationIdentifier(8, 12, true, false)); |
GS1_128_AI.put("8018", new GS1ApplicationIdentifier(0, 18, false, false)); |
GS1_128_AI.put("8020", new GS1ApplicationIdentifier(0, 25, true, false)); |
GS1_128_AI.put("8100", new GS1ApplicationIdentifier(0, 6, false, false)); |
GS1_128_AI.put("8101", new GS1ApplicationIdentifier(0, 10, false, false)); |
GS1_128_AI.put("8102", new GS1ApplicationIdentifier(0, 2, false, false)); |
GS1_128_AI.put("8110", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("8200", new GS1ApplicationIdentifier(0, 70, true, false)); |
GS1_128_AI.put("90", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("91", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("92", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("93", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("94", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("95", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("96", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("97", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("98", new GS1ApplicationIdentifier(0, 30, true, false)); |
GS1_128_AI.put("99", new GS1ApplicationIdentifier(0, 30, true, false)); |
} |
private final ArrayList<String> keysAndValues = new ArrayList<>(); |
public GS1AIElements() { |
} |
public boolean isEmpty() { |
return this.keysAndValues.isEmpty(); |
} |
public boolean containsKey(String key) { |
final int size = this.keysAndValues.size(); |
for (int i = 0; i < size; i += 2) { |
if (this.keysAndValues.get(i).equals(key)) { |
return true; |
} |
} |
return false; |
} |
public boolean containsValue(String value) { |
final int size = this.keysAndValues.size(); |
for (int i = 1; i < size; i += 2) { |
if (this.keysAndValues.get(i).equals(value)) { |
return true; |
} |
} |
return false; |
} |
public String getKey(int index) { |
return this.keysAndValues.get(index * 2); |
} |
public String getValue(int index) { |
return this.keysAndValues.get(1 + index * 2); |
} |
public String get(String key) { |
final int size = this.keysAndValues.size(); |
for (int i = 0; i < size; i += 2) { |
if (this.keysAndValues.get(i).equals(key)) { |
return this.keysAndValues.get(i + 1); |
} |
} |
return null; |
} |
public String put(String key, String value) { |
GS1ApplicationIdentifier ai = GS1_128_AI.get(key); |
if (ai == null) { |
throw new IllegalArgumentException("AI " + key + " unknown"); |
} |
if (ai.variableLength) { |
if (value.length() < ai.minLength) { |
throw new IllegalArgumentException("AI " + key + " value length must >= " + ai.minLength + " but is " + value.length() + " for value " + value); |
} |
if (value.length() > ai.length) { |
throw new IllegalArgumentException("AI " + key + " value length must be <= " + ai.length + " but is " + value.length() + " for value " + value); |
} |
} else { |
if (value.length() != ai.length) { |
throw new IllegalArgumentException("AI " + key + " value length must be " + ai.length + " but is " + value.length() + " for value " + value); |
} |
} |
final int size = this.keysAndValues.size(); |
for (int i = 0; i < size; i += 2) { |
if (this.keysAndValues.get(i).equals(key)) { |
final String old = this.keysAndValues.get(i + 1); |
this.keysAndValues.set(i + 1, value); |
return old; |
} |
} |
this.keysAndValues.add(key); |
this.keysAndValues.add(value); |
return null; |
} |
public String remove(String key) { |
final int size = this.keysAndValues.size(); |
for (int i = 0; i < size; i += 2) { |
if (this.keysAndValues.get(i).equals(key)) { |
this.keysAndValues.remove(i); |
return this.keysAndValues.remove(i); |
} |
} |
return null; |
} |
public void clear() { |
this.keysAndValues.clear(); |
} |
public int size() { |
return this.keysAndValues.size() / 2; |
} |
public void dump(PrintStream out) { |
for (int i = 0; i < size(); i++) { |
out.print("("); |
out.print(getKey(i)); |
out.print(")"); |
out.println(getValue(i)); |
} |
out.flush(); |
} |
public static GS1ApplicationIdentifier getApplicationIdentifier(String k) { |
return GS1_128_AI.get(k); |
} |
public String formatHumanReadable() { |
StringBuilder b = new StringBuilder(); |
int size = size(); |
for (int i = 0; i < size; i++) { |
b.append('('); |
b.append(this.getKey(i)); |
b.append(')'); |
b.append(this.getValue(i)); |
} |
return b.toString(); |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1ApplicationIdentifier.java |
---|
New file |
0,0 → 1,17 |
package org.openconcerto.modules.label.gs1; |
public class GS1ApplicationIdentifier { |
public final int minLength; |
public final int length; |
public final boolean variableLength; |
public final boolean decimalPoint; |
public GS1ApplicationIdentifier(int minLength, int length, boolean variableLength, boolean decimalPoint) { |
this.minLength = minLength; |
this.length = length; |
this.variableLength = variableLength; |
this.decimalPoint = decimalPoint; |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1ParseException.java |
---|
New file |
0,0 → 1,18 |
package org.openconcerto.modules.label.gs1; |
public class GS1ParseException extends Exception { |
final String ai; |
final int errorCode; |
final String errorMessage; |
public GS1ParseException(final String ai, final int errorCode, final String errorMessage) { |
this.ai = ai; |
this.errorCode = errorCode; |
this.errorMessage = errorMessage; |
} |
@Override |
public String toString() { |
return "AI: " + ai + ", errorCode: " + errorCode + ", errorMessage: " + errorMessage; |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1Util.java |
---|
New file |
0,0 → 1,243 |
package org.openconcerto.modules.label.gs1; |
import org.openconcerto.modules.label.ISO646; |
public class GS1Util { |
// ISO/IEC 15424 symbology identifiers |
// ]E0 : EAN-13, UPC-A, or UPC-E : 13 digits |
// ]E1 : Two-digit add-on symbol : 2 digits |
// ]E2 : Five-digit add-on symbol : 5 digits |
// ]E3 : EAN-13, UPC-A, or UPC-E with add-on symbol : 15 or 18 digits |
// ]E4 : EAN-8 : 8 digits |
// ]I1 : ITF-14 : 14 digits |
// ]C1 : GS1-128 : Standard AI element strings |
private static final String GS1_128_SCANNER_PREFIX = "]C1"; |
// ]e0 : GS1 DataBar : Standard AI element strings |
private static final String GS1_DATABAR_SCANNER_PREFIX = "]e0"; |
// ]e1 : GS1 Composite : Data packet containing the data + an encoded symbol separator |
// character. |
// ]e2 : GS1 Composite : Data packet containing the data following an escape mechanism |
// character. |
// ]d2 : GS1 DataMatrix : Standard AI element strings |
private static final String GS1_DATAMATRIX_SCANNER_PREFIX = "]d2"; |
// ]Q3 : GS1 QR Code : Standard AI element strings |
private static final String GS1_QRCODE_SCANNER_PREFIX = "]Q3"; |
// ]J1 : GS1 DotCode : Standard AI element strings |
static final int ERROR_CODE_INVALID_GS1_SCAN = 0; |
static final int ERROR_CODE_UNKNOWN_AI = 1; |
static final int ERROR_CODE_INCOMPLETE_AI = 2; |
static final int ERROR_CODE_NOT_FOUND_SEPARATOR = 3; |
static final int ERROR_CODE_INSUFFICIENT_VALUE_LENGTH = 4; |
static final int ERROR_CODE_EXCEEDED_VALUE_LENGTH = 5; |
static final int ERROR_CODE_CONVERT_DECIMAL_POINT = 5; |
static final int ERROR_CODE_WRONG_DECIMAL_POINT = 6; |
static final int ERROR_CODE_CONVERT_DECIMAL_VALUE = 7; |
public static final char GS_SEPARATOR = '\u001D'; |
// public static final char FNC1_SEPARATOR = 232; |
static final int SPACE_SEPARATOR = ' '; |
int separator; |
public GS1Util() { |
this(GS_SEPARATOR); |
} |
public GS1Util(int separator) { |
this.separator = separator; |
} |
public GS1AIElements parseFromScanner(String scan) throws GS1ParseException { |
if (scan.startsWith(GS1_DATAMATRIX_SCANNER_PREFIX) || scan.startsWith(GS1_128_SCANNER_PREFIX) || scan.startsWith(GS1_DATABAR_SCANNER_PREFIX) || scan.startsWith(GS1_QRCODE_SCANNER_PREFIX)) { |
return parse(scan.substring(3)); |
} |
return parse(scan); |
} |
public GS1AIElements parse(String barcode) throws GS1ParseException { |
if (barcode.length() < 3) { |
throw new GS1ParseException("", ERROR_CODE_INVALID_GS1_SCAN, "code too short"); |
} |
System.err.println("GS1Util.parse()" + barcode); |
GS1AIElements attr = new GS1AIElements(); |
StringBuilder ai = new StringBuilder(); |
int length = barcode.length(); |
for (int i = 0; i < length; ++i) { |
int aiLength = ai.length(); |
if (aiLength > 1) { |
GS1ApplicationIdentifier aii = GS1AIElements.getApplicationIdentifier(ai.toString()); |
if (aii == null) { |
if (aiLength < 4) |
ai.append(barcode.charAt(i)); |
else |
throw new GS1ParseException(ai.toString(), ERROR_CODE_UNKNOWN_AI, "Unknown AI"); |
} else { |
int decimalPoint = 0; |
if (aii.decimalPoint) { |
try { |
decimalPoint = Integer.valueOf(String.valueOf(barcode.charAt(i))); |
} catch (NumberFormatException e) { |
throw new GS1ParseException(ai.toString(), ERROR_CODE_CONVERT_DECIMAL_POINT, "Errow convert to decimal point"); |
} |
if (++i >= length) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length"); |
} |
String value; |
if (aii.variableLength) { |
int separatorIndex = barcode.indexOf(this.separator, i); |
if (separatorIndex < 0) { |
if (length - i > aii.length) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_NOT_FOUND_SEPARATOR, "Not found separator"); |
else if (length - i < aii.minLength) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length"); |
else { |
value = barcode.substring(i); |
i = length; |
} |
} else if (separatorIndex - i > aii.length) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_EXCEEDED_VALUE_LENGTH, "Exceeded value length"); |
else if (separatorIndex - i < aii.minLength) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length"); |
else { |
value = barcode.substring(i, separatorIndex); |
i = separatorIndex; |
} |
} else { |
if (i + aii.length > length) { |
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length"); |
} |
value = barcode.substring(i, i + aii.length); |
i += aii.length - 1; |
} |
if (aii.decimalPoint && decimalPoint > 0) { |
if (decimalPoint >= value.length()) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_WRONG_DECIMAL_POINT, "Decimal point more then value length"); |
try { |
value = String.valueOf(Double.valueOf(value.substring(0, value.length() - decimalPoint) + "." + value.substring(value.length() - decimalPoint))); |
} catch (NumberFormatException e) { |
throw new GS1ParseException(ai.toString(), ERROR_CODE_CONVERT_DECIMAL_VALUE, "Error convert decimal point value"); |
} |
} |
attr.put(ai.toString(), value); |
ai.setLength(0); |
} |
} else |
ai.append(barcode.charAt(i)); |
} |
if (ai.length() > 0) |
throw new GS1ParseException(ai.toString(), ERROR_CODE_INCOMPLETE_AI, "Incomplete AI"); |
return attr; |
} |
public String format(GS1AIElements values) { |
StringBuilder b = new StringBuilder(); |
int size = values.size(); |
for (int i = 0; i < size; i++) { |
String k = values.getKey(i); |
GS1ApplicationIdentifier ai = GS1AIElements.getApplicationIdentifier(k); |
final String value = values.getValue(i); |
b.append(k); |
b.append(value); |
if (ai.variableLength && i < size - 1) { |
b.append((char) this.separator); |
} |
} |
return b.toString(); |
} |
public String formatZPL(GS1AIElements values) { |
StringBuilder b = new StringBuilder(); |
int size = values.size(); |
b.append("@1"); |
for (int i = 0; i < size; i++) { |
String k = values.getKey(i); |
GS1ApplicationIdentifier ai = GS1AIElements.getApplicationIdentifier(k); |
final String value = values.getValue(i); |
b.append(k); |
b.append(value); |
if (ai.variableLength && i < size - 1) { |
b.append("@d029"); |
} |
} |
return b.toString(); |
} |
public String formatDataMatrix(GS1AIElements values) { |
StringBuilder b = new StringBuilder(); |
int size = values.size(); |
// b.append(FNC1_SEPARATOR); |
for (int i = 0; i < size; i++) { |
String k = values.getKey(i); |
GS1ApplicationIdentifier ai = GS1AIElements.getApplicationIdentifier(k); |
final String value = values.getValue(i); |
b.append('['); |
b.append(k); |
b.append(']'); |
b.append(value); |
if (ai.variableLength && i < size - 1) { |
// b.append(GS_SEPARATOR); |
} |
} |
return b.toString(); |
} |
public static void main(String[] args) throws GS1ParseException { |
GS1Util p = new GS1Util('_'); |
System.out.println("GS1Util.main()" + (char) p.separator); |
String barcode = "0104607018700852111806051718062910180605_211"; |
// barcode = "01088888931021461712031510W1040190"; |
GS1AIElements values = p.parse(barcode); |
values.dump(System.out); |
values.put("391", "kookkk"); |
System.out.println(p.format(values)); |
System.out.println(values.formatHumanReadable()); |
GS1AIElements valuesMax = new GS1AIElements(); |
valuesMax.put("02", "01234567891234"); |
valuesMax.put("15", "201202"); |
valuesMax.put("37", "12345678"); |
valuesMax.put("10", "12345678901234567890"); |
GS1Util pStd = new GS1Util(); |
System.out.println(p.format(valuesMax)); |
System.out.println(p.format(valuesMax).length()); |
System.out.println(pStd.format(valuesMax)); |
System.out.println(pStd.format(valuesMax).length()); |
System.err.println("GS1Util.main() GS128 from barcode reader"); |
String gs1128 = "]C10207612345678900152012153745646578"; |
GS1Util p2 = new GS1Util(); |
values = p2.parseFromScanner(gs1128); |
values.dump(System.out); |
} |
public static String showAllChars(String s) { |
StringBuilder b = new StringBuilder(); |
for (int i = 0; i < s.length(); i++) { |
if (ISO646.isValid(s.charAt(i))) { |
b.append(s.charAt(i)); |
} else { |
b.append("[" + (int) s.charAt(i) + "]"); |
} |
} |
return b.toString(); |
} |
} |
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/graphicspl/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/uk/org/okapibarcode/backend/Upc.java |
---|
New file |
0,0 → 1,411 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.backend.Ean.calcDigit; |
import static uk.org.okapibarcode.backend.Ean.validateAndPad; |
import static uk.org.okapibarcode.backend.HumanReadableLocation.BOTTOM; |
import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE; |
import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP; |
import java.awt.geom.Rectangle2D; |
import java.util.Arrays; |
/** |
* <p> |
* Implements UPC bar code symbology according to BS EN 797:1996. |
* |
* <p> |
* UPC-A requires an 11 digit article number. The check digit is calculated. UPC-E is a |
* zero-compressed version of UPC-A developed for smaller packages. The code requires a 6 digit |
* article number (digits 0-9). The check digit is calculated. Also supports Number System 1 |
* encoding by entering a 7-digit article number stating with the digit 1. |
* |
* <p> |
* EAN-2 and EAN-5 add-on symbols can be added using the '+' character followed by the add-on data. |
* |
* @author <a href="mailto:jakel2006@me.com">Robert Elliott</a> |
*/ |
public class Upc extends Symbol { |
public static enum Mode { |
UPCA, UPCE |
}; |
private static final String[] SET_AC = { "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213", "3112" }; |
private static final String[] SET_B = { "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121", "2113" }; |
/* Number set for UPC-E symbol (EN Table 4) */ |
private static final String[] UPC_PARITY_0 = { "BBBAAA", "BBABAA", "BBAABA", "BBAAAB", "BABBAA", "BAABBA", "BAAABB", "BABABA", "BABAAB", "BAABAB" }; |
/* Not covered by BS EN 797 */ |
private static final String[] UPC_PARITY_1 = { "AAABBB", "AABABB", "AABBAB", "AABBBA", "ABAABB", "ABBAAB", "ABBBAA", "ABABAB", "ABABBA", "ABBABA" }; |
private Mode mode = Mode.UPCA; |
private boolean showCheckDigit = true; |
private int guardPatternExtraHeight = 5; |
private boolean linkageFlag; |
private EanUpcAddOn addOn; |
/** Creates a new instance. */ |
public Upc() { |
this.humanReadableAlignment = HumanReadableAlignment.JUSTIFY; |
} |
/** |
* Sets the UPC mode (UPC-A or UPC-E). The default is UPC-A. |
* |
* @param mode the UPC mode (UPC-A or UPC-E) |
*/ |
public void setMode(final Mode mode) { |
this.mode = mode; |
} |
/** |
* Returns the UPC mode (UPC-A or UPC-E). |
* |
* @return the UPC mode (UPC-A or UPC-E) |
*/ |
public Mode getMode() { |
return this.mode; |
} |
/** |
* Sets whether or not to show the check digit in the human-readable text. |
* |
* @param showCheckDigit whether or not to show the check digit in the human-readable text |
*/ |
public void setShowCheckDigit(final boolean showCheckDigit) { |
this.showCheckDigit = showCheckDigit; |
} |
/** |
* Returns whether or not to show the check digit in the human-readable text. |
* |
* @return whether or not to show the check digit in the human-readable text |
*/ |
public boolean getShowCheckDigit() { |
return this.showCheckDigit; |
} |
/** |
* Sets the extra height used for the guard patterns. The default value is <code>5</code>. |
* |
* @param guardPatternExtraHeight the extra height used for the guard patterns |
*/ |
public void setGuardPatternExtraHeight(final int guardPatternExtraHeight) { |
this.guardPatternExtraHeight = guardPatternExtraHeight; |
} |
/** |
* Returns the extra height used for the guard patterns. |
* |
* @return the extra height used for the guard patterns |
*/ |
public int getGuardPatternExtraHeight() { |
return this.guardPatternExtraHeight; |
} |
/** |
* Sets the linkage flag. If set to <code>true</code>, this symbol is part of a composite |
* symbol. |
* |
* @param linkageFlag the linkage flag |
*/ |
protected void setLinkageFlag(final boolean linkageFlag) { |
this.linkageFlag = linkageFlag; |
} |
@Override |
protected void encode() { |
separateContent(); |
if (this.content.isEmpty()) { |
throw new OkapiException("Missing UPC data"); |
} |
if (this.mode == Mode.UPCA) { |
upca(); |
} else { |
upce(); |
} |
} |
private void separateContent() { |
final int splitPoint = this.content.indexOf('+'); |
if (splitPoint == -1) { |
// there is no add-on data |
this.addOn = null; |
} else if (splitPoint == this.content.length() - 1) { |
// we found the add-on separator, but no add-on data |
throw new OkapiException("Invalid add-on data"); |
} else { |
// there is a '+' in the input data, use an add-on EAN2 or EAN5 |
this.addOn = new EanUpcAddOn(); |
this.addOn.font = this.font; |
this.addOn.fontName = this.fontName; |
this.addOn.fontSize = this.fontSize; |
this.addOn.humanReadableLocation = this.humanReadableLocation == NONE ? NONE : TOP; |
this.addOn.moduleWidth = this.moduleWidth; |
this.addOn.default_height = this.default_height + this.guardPatternExtraHeight - 8; |
this.addOn.setContent(this.content.substring(splitPoint + 1)); |
this.content = this.content.substring(0, splitPoint); |
} |
} |
private void upca() { |
this.content = validateAndPad(this.content, 11); |
final char check = calcDigit(this.content); |
infoLine("Check Digit: " + check); |
final String hrt = this.content + check; |
final StringBuilder dest = new StringBuilder("111"); |
for (int i = 0; i < 12; i++) { |
if (i == 6) { |
dest.append("11111"); |
} |
dest.append(SET_AC[hrt.charAt(i) - '0']); |
} |
dest.append("111"); |
this.readable = hrt; |
this.pattern = new String[] { dest.toString() }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
private void upce() { |
this.content = validateAndPad(this.content, 7); |
final String expanded = expandToEquivalentUpcA(this.content, true); |
infoLine("UPC-A Equivalent: " + expanded); |
final char check = calcDigit(expanded); |
infoLine("Check Digit: " + check); |
final String hrt = this.content + check; |
final int numberSystem = getNumberSystem(this.content); |
final String[] parityArray = numberSystem == 1 ? UPC_PARITY_1 : UPC_PARITY_0; |
final String parity = parityArray[check - '0']; |
final StringBuilder dest = new StringBuilder("111"); |
for (int i = 0; i < 6; i++) { |
if (parity.charAt(i) == 'A') { |
dest.append(SET_AC[this.content.charAt(i + 1) - '0']); |
} else { // B |
dest.append(SET_B[this.content.charAt(i + 1) - '0']); |
} |
} |
dest.append("111111"); |
this.readable = hrt; |
this.pattern = new String[] { dest.toString() }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
/** |
* Expands the zero-compressed UPC-E code to make a UPC-A equivalent (EN Table 5). |
* |
* @param content the UPC-E code to expand |
* @param validate whether or not to validate the input |
* @return the UPC-A equivalent of the specified UPC-E code |
*/ |
protected String expandToEquivalentUpcA(final String content, final boolean validate) { |
final char[] upce = content.toCharArray(); |
final char[] upca = new char[11]; |
Arrays.fill(upca, '0'); |
upca[0] = upce[0]; |
upca[1] = upce[1]; |
upca[2] = upce[2]; |
final char emode = upce[6]; |
switch (emode) { |
case '0': |
case '1': |
case '2': |
upca[3] = emode; |
upca[8] = upce[3]; |
upca[9] = upce[4]; |
upca[10] = upce[5]; |
break; |
case '3': |
upca[3] = upce[3]; |
upca[9] = upce[4]; |
upca[10] = upce[5]; |
if (validate && (upce[3] == '0' || upce[3] == '1' || upce[3] == '2')) { |
/* Note 1 - "X3 shall not be equal to 0, 1 or 2" */ |
throw new OkapiException("Invalid UPC-E data"); |
} |
break; |
case '4': |
upca[3] = upce[3]; |
upca[4] = upce[4]; |
upca[10] = upce[5]; |
if (validate && upce[4] == '0') { |
/* Note 2 - "X4 shall not be equal to 0" */ |
throw new OkapiException("Invalid UPC-E data"); |
} |
break; |
default: |
upca[3] = upce[3]; |
upca[4] = upce[4]; |
upca[5] = upce[5]; |
upca[10] = emode; |
if (validate && upce[5] == '0') { |
/* Note 3 - "X5 shall not be equal to 0" */ |
throw new OkapiException("Invalid UPC-E data"); |
} |
break; |
} |
return new String(upca); |
} |
/** Two number systems can be used: system 0 and system 1. */ |
private static int getNumberSystem(final String content) { |
switch (content.charAt(0)) { |
case '0': |
return 0; |
case '1': |
return 1; |
default: |
throw new OkapiException("Invalid input data"); |
} |
} |
@Override |
protected void plotSymbol() { |
int xBlock; |
int x, y, w, h; |
boolean black = true; |
final int compositeOffset = this.linkageFlag ? 6 : 0; // space for composite separator above |
final int hrtOffset = this.humanReadableLocation == TOP ? getTheoreticalHumanReadableHeight() : 0; // space |
// for |
// HRT |
// above |
this.rectangles.clear(); |
this.texts.clear(); |
x = 0; |
/* Draw the bars in the symbology */ |
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) { |
w = this.pattern[0].charAt(xBlock) - '0'; |
if (black) { |
y = 0; |
h = this.default_height; |
/* Add extension to guide bars */ |
if (this.mode == Mode.UPCA) { |
if (x < 10 || x > 84 || x > 45 && x < 49) { |
h += this.guardPatternExtraHeight; |
} |
if (this.linkageFlag && (x == 0 || x == 94)) { |
h += 2; |
y -= 2; |
} |
} else { |
if (x < 4 || x > 45) { |
h += this.guardPatternExtraHeight; |
} |
if (this.linkageFlag && (x == 0 || x == 50)) { |
h += 2; |
y -= 2; |
} |
} |
final Rectangle2D.Double rect = new Rectangle2D.Double(scale(x), y + compositeOffset + hrtOffset, scale(w), h); |
this.rectangles.add(rect); |
this.symbol_width = Math.max(this.symbol_width, (int) rect.getMaxX()); |
this.symbol_height = Math.max(this.symbol_height, (int) rect.getHeight()); |
} |
black = !black; |
x += w; |
} |
/* Add separator for composite symbology, if necessary */ |
if (this.linkageFlag) { |
if (this.mode == Mode.UPCA) { |
this.rectangles.add(new Rectangle2D.Double(scale(0), 0, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(94), 0, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(-1), 2, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(95), 2, scale(1), 2)); |
} else { // UPCE |
this.rectangles.add(new Rectangle2D.Double(scale(0), 0, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(50), 0, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(-1), 2, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(51), 2, scale(1), 2)); |
} |
this.symbol_height += 4; |
} |
/* Now add the text */ |
if (this.humanReadableLocation == BOTTOM) { |
this.symbol_height -= this.guardPatternExtraHeight; |
final double baseline = this.symbol_height + this.fontSize; |
if (this.mode == Mode.UPCA) { |
this.texts.add(new TextBox(scale(-9), baseline, scale(4), this.readable.substring(0, 1), HumanReadableAlignment.RIGHT)); |
this.texts.add(new TextBox(scale(12), baseline, scale(32), this.readable.substring(1, 6), this.humanReadableAlignment)); |
this.texts.add(new TextBox(scale(51), baseline, scale(32), this.readable.substring(6, 11), this.humanReadableAlignment)); |
if (this.showCheckDigit) { |
this.texts.add(new TextBox(scale(97), baseline, scale(4), this.readable.substring(11, 12), HumanReadableAlignment.LEFT)); |
} |
} else { // UPCE |
this.texts.add(new TextBox(scale(-9), baseline, scale(4), this.readable.substring(0, 1), HumanReadableAlignment.RIGHT)); |
this.texts.add(new TextBox(scale(5), baseline, scale(39), this.readable.substring(1, 7), this.humanReadableAlignment)); |
if (this.showCheckDigit) { |
this.texts.add(new TextBox(scale(53), baseline, scale(4), this.readable.substring(7, 8), HumanReadableAlignment.LEFT)); |
} |
} |
} else if (this.humanReadableLocation == TOP) { |
final double baseline = this.fontSize; |
final int width = this.mode == Mode.UPCA ? 94 : 50; |
this.texts.add(new TextBox(scale(0), baseline, scale(width), this.readable, this.humanReadableAlignment)); |
} |
/* Now add the add-on symbol, if necessary */ |
if (this.addOn != null) { |
final int gap = 9; |
final int baseX = this.symbol_width + scale(gap); |
final Rectangle2D.Double r1 = this.rectangles.get(0); |
final Rectangle2D.Double ar1 = this.addOn.rectangles.get(0); |
final int baseY = (int) (r1.y + r1.getHeight() - ar1.y - ar1.getHeight()); |
for (final TextBox t : this.addOn.getTexts()) { |
this.texts.add(new TextBox(baseX + t.x, baseY + t.y, t.width, t.text, t.alignment)); |
} |
for (final Rectangle2D.Double r : this.addOn.getRectangles()) { |
this.rectangles.add(new Rectangle2D.Double(baseX + r.x, baseY + r.y, r.width, r.height)); |
} |
this.symbol_width += scale(gap) + this.addOn.symbol_width; |
this.pattern[0] = this.pattern[0] + gap + this.addOn.pattern[0]; |
} |
} |
/** Scales the specified width or x-dimension according to the current module width. */ |
private int scale(final int w) { |
return this.moduleWidth * w; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/UspsOneCode.java |
---|
New file |
0,0 → 1,458 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE; |
import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP; |
import java.awt.geom.Rectangle2D; |
import java.math.BigInteger; |
/** |
* <p> |
* Implements USPS OneCode (also known as Intelligent Mail Barcode) according to USPS-B-3200F. |
* |
* <p> |
* OneCode is a fixed length (65-bar) symbol which combines routing and customer information in a |
* single symbol. Input data consists of a 20 digit tracking code, followed by a dash (-), followed |
* by a delivery point ZIP code which can be 0, 5, 9 or 11 digits in length. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
* @see <a href= |
* "https://ribbs.usps.gov/intelligentmail_mailpieces/documents/tech_guides/SPUSPSG.pdf">USPS |
* OneCode Specification</a> |
*/ |
public class UspsOneCode extends Symbol { |
/* The following lookup tables were generated using the code in Appendix C */ |
/** Appendix D Table 1 - 5 of 13 characters */ |
private static final int[] APPX_D_I = { 0x001F, 0x1F00, 0x002F, 0x1E80, 0x0037, 0x1D80, 0x003B, 0x1B80, 0x003D, 0x1780, 0x003E, 0x0F80, 0x004F, 0x1E40, 0x0057, 0x1D40, 0x005B, 0x1B40, 0x005D, |
0x1740, 0x005E, 0x0F40, 0x0067, 0x1CC0, 0x006B, 0x1AC0, 0x006D, 0x16C0, 0x006E, 0x0EC0, 0x0073, 0x19C0, 0x0075, 0x15C0, 0x0076, 0x0DC0, 0x0079, 0x13C0, 0x007A, 0x0BC0, 0x007C, 0x07C0, |
0x008F, 0x1E20, 0x0097, 0x1D20, 0x009B, 0x1B20, 0x009D, 0x1720, 0x009E, 0x0F20, 0x00A7, 0x1CA0, 0x00AB, 0x1AA0, 0x00AD, 0x16A0, 0x00AE, 0x0EA0, 0x00B3, 0x19A0, 0x00B5, 0x15A0, 0x00B6, |
0x0DA0, 0x00B9, 0x13A0, 0x00BA, 0x0BA0, 0x00BC, 0x07A0, 0x00C7, 0x1C60, 0x00CB, 0x1A60, 0x00CD, 0x1660, 0x00CE, 0x0E60, 0x00D3, 0x1960, 0x00D5, 0x1560, 0x00D6, 0x0D60, 0x00D9, 0x1360, |
0x00DA, 0x0B60, 0x00DC, 0x0760, 0x00E3, 0x18E0, 0x00E5, 0x14E0, 0x00E6, 0x0CE0, 0x00E9, 0x12E0, 0x00EA, 0x0AE0, 0x00EC, 0x06E0, 0x00F1, 0x11E0, 0x00F2, 0x09E0, 0x00F4, 0x05E0, 0x00F8, |
0x03E0, 0x010F, 0x1E10, 0x0117, 0x1D10, 0x011B, 0x1B10, 0x011D, 0x1710, 0x011E, 0x0F10, 0x0127, 0x1C90, 0x012B, 0x1A90, 0x012D, 0x1690, 0x012E, 0x0E90, 0x0133, 0x1990, 0x0135, 0x1590, |
0x0136, 0x0D90, 0x0139, 0x1390, 0x013A, 0x0B90, 0x013C, 0x0790, 0x0147, 0x1C50, 0x014B, 0x1A50, 0x014D, 0x1650, 0x014E, 0x0E50, 0x0153, 0x1950, 0x0155, 0x1550, 0x0156, 0x0D50, 0x0159, |
0x1350, 0x015A, 0x0B50, 0x015C, 0x0750, 0x0163, 0x18D0, 0x0165, 0x14D0, 0x0166, 0x0CD0, 0x0169, 0x12D0, 0x016A, 0x0AD0, 0x016C, 0x06D0, 0x0171, 0x11D0, 0x0172, 0x09D0, 0x0174, 0x05D0, |
0x0178, 0x03D0, 0x0187, 0x1C30, 0x018B, 0x1A30, 0x018D, 0x1630, 0x018E, 0x0E30, 0x0193, 0x1930, 0x0195, 0x1530, 0x0196, 0x0D30, 0x0199, 0x1330, 0x019A, 0x0B30, 0x019C, 0x0730, 0x01A3, |
0x18B0, 0x01A5, 0x14B0, 0x01A6, 0x0CB0, 0x01A9, 0x12B0, 0x01AA, 0x0AB0, 0x01AC, 0x06B0, 0x01B1, 0x11B0, 0x01B2, 0x09B0, 0x01B4, 0x05B0, 0x01B8, 0x03B0, 0x01C3, 0x1870, 0x01C5, 0x1470, |
0x01C6, 0x0C70, 0x01C9, 0x1270, 0x01CA, 0x0A70, 0x01CC, 0x0670, 0x01D1, 0x1170, 0x01D2, 0x0970, 0x01D4, 0x0570, 0x01D8, 0x0370, 0x01E1, 0x10F0, 0x01E2, 0x08F0, 0x01E4, 0x04F0, 0x01E8, |
0x02F0, 0x020F, 0x1E08, 0x0217, 0x1D08, 0x021B, 0x1B08, 0x021D, 0x1708, 0x021E, 0x0F08, 0x0227, 0x1C88, 0x022B, 0x1A88, 0x022D, 0x1688, 0x022E, 0x0E88, 0x0233, 0x1988, 0x0235, 0x1588, |
0x0236, 0x0D88, 0x0239, 0x1388, 0x023A, 0x0B88, 0x023C, 0x0788, 0x0247, 0x1C48, 0x024B, 0x1A48, 0x024D, 0x1648, 0x024E, 0x0E48, 0x0253, 0x1948, 0x0255, 0x1548, 0x0256, 0x0D48, 0x0259, |
0x1348, 0x025A, 0x0B48, 0x025C, 0x0748, 0x0263, 0x18C8, 0x0265, 0x14C8, 0x0266, 0x0CC8, 0x0269, 0x12C8, 0x026A, 0x0AC8, 0x026C, 0x06C8, 0x0271, 0x11C8, 0x0272, 0x09C8, 0x0274, 0x05C8, |
0x0278, 0x03C8, 0x0287, 0x1C28, 0x028B, 0x1A28, 0x028D, 0x1628, 0x028E, 0x0E28, 0x0293, 0x1928, 0x0295, 0x1528, 0x0296, 0x0D28, 0x0299, 0x1328, 0x029A, 0x0B28, 0x029C, 0x0728, 0x02A3, |
0x18A8, 0x02A5, 0x14A8, 0x02A6, 0x0CA8, 0x02A9, 0x12A8, 0x02AA, 0x0AA8, 0x02AC, 0x06A8, 0x02B1, 0x11A8, 0x02B2, 0x09A8, 0x02B4, 0x05A8, 0x02B8, 0x03A8, 0x02C3, 0x1868, 0x02C5, 0x1468, |
0x02C6, 0x0C68, 0x02C9, 0x1268, 0x02CA, 0x0A68, 0x02CC, 0x0668, 0x02D1, 0x1168, 0x02D2, 0x0968, 0x02D4, 0x0568, 0x02D8, 0x0368, 0x02E1, 0x10E8, 0x02E2, 0x08E8, 0x02E4, 0x04E8, 0x0307, |
0x1C18, 0x030B, 0x1A18, 0x030D, 0x1618, 0x030E, 0x0E18, 0x0313, 0x1918, 0x0315, 0x1518, 0x0316, 0x0D18, 0x0319, 0x1318, 0x031A, 0x0B18, 0x031C, 0x0718, 0x0323, 0x1898, 0x0325, 0x1498, |
0x0326, 0x0C98, 0x0329, 0x1298, 0x032A, 0x0A98, 0x032C, 0x0698, 0x0331, 0x1198, 0x0332, 0x0998, 0x0334, 0x0598, 0x0338, 0x0398, 0x0343, 0x1858, 0x0345, 0x1458, 0x0346, 0x0C58, 0x0349, |
0x1258, 0x034A, 0x0A58, 0x034C, 0x0658, 0x0351, 0x1158, 0x0352, 0x0958, 0x0354, 0x0558, 0x0361, 0x10D8, 0x0362, 0x08D8, 0x0364, 0x04D8, 0x0383, 0x1838, 0x0385, 0x1438, 0x0386, 0x0C38, |
0x0389, 0x1238, 0x038A, 0x0A38, 0x038C, 0x0638, 0x0391, 0x1138, 0x0392, 0x0938, 0x0394, 0x0538, 0x03A1, 0x10B8, 0x03A2, 0x08B8, 0x03A4, 0x04B8, 0x03C1, 0x1078, 0x03C2, 0x0878, 0x03C4, |
0x0478, 0x040F, 0x1E04, 0x0417, 0x1D04, 0x041B, 0x1B04, 0x041D, 0x1704, 0x041E, 0x0F04, 0x0427, 0x1C84, 0x042B, 0x1A84, 0x042D, 0x1684, 0x042E, 0x0E84, 0x0433, 0x1984, 0x0435, 0x1584, |
0x0436, 0x0D84, 0x0439, 0x1384, 0x043A, 0x0B84, 0x043C, 0x0784, 0x0447, 0x1C44, 0x044B, 0x1A44, 0x044D, 0x1644, 0x044E, 0x0E44, 0x0453, 0x1944, 0x0455, 0x1544, 0x0456, 0x0D44, 0x0459, |
0x1344, 0x045A, 0x0B44, 0x045C, 0x0744, 0x0463, 0x18C4, 0x0465, 0x14C4, 0x0466, 0x0CC4, 0x0469, 0x12C4, 0x046A, 0x0AC4, 0x046C, 0x06C4, 0x0471, 0x11C4, 0x0472, 0x09C4, 0x0474, 0x05C4, |
0x0487, 0x1C24, 0x048B, 0x1A24, 0x048D, 0x1624, 0x048E, 0x0E24, 0x0493, 0x1924, 0x0495, 0x1524, 0x0496, 0x0D24, 0x0499, 0x1324, 0x049A, 0x0B24, 0x049C, 0x0724, 0x04A3, 0x18A4, 0x04A5, |
0x14A4, 0x04A6, 0x0CA4, 0x04A9, 0x12A4, 0x04AA, 0x0AA4, 0x04AC, 0x06A4, 0x04B1, 0x11A4, 0x04B2, 0x09A4, 0x04B4, 0x05A4, 0x04C3, 0x1864, 0x04C5, 0x1464, 0x04C6, 0x0C64, 0x04C9, 0x1264, |
0x04CA, 0x0A64, 0x04CC, 0x0664, 0x04D1, 0x1164, 0x04D2, 0x0964, 0x04D4, 0x0564, 0x04E1, 0x10E4, 0x04E2, 0x08E4, 0x0507, 0x1C14, 0x050B, 0x1A14, 0x050D, 0x1614, 0x050E, 0x0E14, 0x0513, |
0x1914, 0x0515, 0x1514, 0x0516, 0x0D14, 0x0519, 0x1314, 0x051A, 0x0B14, 0x051C, 0x0714, 0x0523, 0x1894, 0x0525, 0x1494, 0x0526, 0x0C94, 0x0529, 0x1294, 0x052A, 0x0A94, 0x052C, 0x0694, |
0x0531, 0x1194, 0x0532, 0x0994, 0x0534, 0x0594, 0x0543, 0x1854, 0x0545, 0x1454, 0x0546, 0x0C54, 0x0549, 0x1254, 0x054A, 0x0A54, 0x054C, 0x0654, 0x0551, 0x1154, 0x0552, 0x0954, 0x0561, |
0x10D4, 0x0562, 0x08D4, 0x0583, 0x1834, 0x0585, 0x1434, 0x0586, 0x0C34, 0x0589, 0x1234, 0x058A, 0x0A34, 0x058C, 0x0634, 0x0591, 0x1134, 0x0592, 0x0934, 0x05A1, 0x10B4, 0x05A2, 0x08B4, |
0x05C1, 0x1074, 0x05C2, 0x0874, 0x0607, 0x1C0C, 0x060B, 0x1A0C, 0x060D, 0x160C, 0x060E, 0x0E0C, 0x0613, 0x190C, 0x0615, 0x150C, 0x0616, 0x0D0C, 0x0619, 0x130C, 0x061A, 0x0B0C, 0x061C, |
0x070C, 0x0623, 0x188C, 0x0625, 0x148C, 0x0626, 0x0C8C, 0x0629, 0x128C, 0x062A, 0x0A8C, 0x062C, 0x068C, 0x0631, 0x118C, 0x0632, 0x098C, 0x0643, 0x184C, 0x0645, 0x144C, 0x0646, 0x0C4C, |
0x0649, 0x124C, 0x064A, 0x0A4C, 0x0651, 0x114C, 0x0652, 0x094C, 0x0661, 0x10CC, 0x0662, 0x08CC, 0x0683, 0x182C, 0x0685, 0x142C, 0x0686, 0x0C2C, 0x0689, 0x122C, 0x068A, 0x0A2C, 0x0691, |
0x112C, 0x0692, 0x092C, 0x06A1, 0x10AC, 0x06A2, 0x08AC, 0x06C1, 0x106C, 0x06C2, 0x086C, 0x0703, 0x181C, 0x0705, 0x141C, 0x0706, 0x0C1C, 0x0709, 0x121C, 0x070A, 0x0A1C, 0x0711, 0x111C, |
0x0712, 0x091C, 0x0721, 0x109C, 0x0722, 0x089C, 0x0741, 0x105C, 0x0742, 0x085C, 0x0781, 0x103C, 0x0782, 0x083C, 0x080F, 0x1E02, 0x0817, 0x1D02, 0x081B, 0x1B02, 0x081D, 0x1702, 0x081E, |
0x0F02, 0x0827, 0x1C82, 0x082B, 0x1A82, 0x082D, 0x1682, 0x082E, 0x0E82, 0x0833, 0x1982, 0x0835, 0x1582, 0x0836, 0x0D82, 0x0839, 0x1382, 0x083A, 0x0B82, 0x0847, 0x1C42, 0x084B, 0x1A42, |
0x084D, 0x1642, 0x084E, 0x0E42, 0x0853, 0x1942, 0x0855, 0x1542, 0x0856, 0x0D42, 0x0859, 0x1342, 0x085A, 0x0B42, 0x0863, 0x18C2, 0x0865, 0x14C2, 0x0866, 0x0CC2, 0x0869, 0x12C2, 0x086A, |
0x0AC2, 0x0871, 0x11C2, 0x0872, 0x09C2, 0x0887, 0x1C22, 0x088B, 0x1A22, 0x088D, 0x1622, 0x088E, 0x0E22, 0x0893, 0x1922, 0x0895, 0x1522, 0x0896, 0x0D22, 0x0899, 0x1322, 0x089A, 0x0B22, |
0x08A3, 0x18A2, 0x08A5, 0x14A2, 0x08A6, 0x0CA2, 0x08A9, 0x12A2, 0x08AA, 0x0AA2, 0x08B1, 0x11A2, 0x08B2, 0x09A2, 0x08C3, 0x1862, 0x08C5, 0x1462, 0x08C6, 0x0C62, 0x08C9, 0x1262, 0x08CA, |
0x0A62, 0x08D1, 0x1162, 0x08D2, 0x0962, 0x08E1, 0x10E2, 0x0907, 0x1C12, 0x090B, 0x1A12, 0x090D, 0x1612, 0x090E, 0x0E12, 0x0913, 0x1912, 0x0915, 0x1512, 0x0916, 0x0D12, 0x0919, 0x1312, |
0x091A, 0x0B12, 0x0923, 0x1892, 0x0925, 0x1492, 0x0926, 0x0C92, 0x0929, 0x1292, 0x092A, 0x0A92, 0x0931, 0x1192, 0x0932, 0x0992, 0x0943, 0x1852, 0x0945, 0x1452, 0x0946, 0x0C52, 0x0949, |
0x1252, 0x094A, 0x0A52, 0x0951, 0x1152, 0x0961, 0x10D2, 0x0983, 0x1832, 0x0985, 0x1432, 0x0986, 0x0C32, 0x0989, 0x1232, 0x098A, 0x0A32, 0x0991, 0x1132, 0x09A1, 0x10B2, 0x09C1, 0x1072, |
0x0A07, 0x1C0A, 0x0A0B, 0x1A0A, 0x0A0D, 0x160A, 0x0A0E, 0x0E0A, 0x0A13, 0x190A, 0x0A15, 0x150A, 0x0A16, 0x0D0A, 0x0A19, 0x130A, 0x0A1A, 0x0B0A, 0x0A23, 0x188A, 0x0A25, 0x148A, 0x0A26, |
0x0C8A, 0x0A29, 0x128A, 0x0A2A, 0x0A8A, 0x0A31, 0x118A, 0x0A43, 0x184A, 0x0A45, 0x144A, 0x0A46, 0x0C4A, 0x0A49, 0x124A, 0x0A51, 0x114A, 0x0A61, 0x10CA, 0x0A83, 0x182A, 0x0A85, 0x142A, |
0x0A86, 0x0C2A, 0x0A89, 0x122A, 0x0A91, 0x112A, 0x0AA1, 0x10AA, 0x0AC1, 0x106A, 0x0B03, 0x181A, 0x0B05, 0x141A, 0x0B06, 0x0C1A, 0x0B09, 0x121A, 0x0B11, 0x111A, 0x0B21, 0x109A, 0x0B41, |
0x105A, 0x0B81, 0x103A, 0x0C07, 0x1C06, 0x0C0B, 0x1A06, 0x0C0D, 0x1606, 0x0C0E, 0x0E06, 0x0C13, 0x1906, 0x0C15, 0x1506, 0x0C16, 0x0D06, 0x0C19, 0x1306, 0x0C23, 0x1886, 0x0C25, 0x1486, |
0x0C26, 0x0C86, 0x0C29, 0x1286, 0x0C31, 0x1186, 0x0C43, 0x1846, 0x0C45, 0x1446, 0x0C49, 0x1246, 0x0C51, 0x1146, 0x0C61, 0x10C6, 0x0C83, 0x1826, 0x0C85, 0x1426, 0x0C89, 0x1226, 0x0C91, |
0x1126, 0x0CA1, 0x10A6, 0x0CC1, 0x1066, 0x0D03, 0x1816, 0x0D05, 0x1416, 0x0D09, 0x1216, 0x0D11, 0x1116, 0x0D21, 0x1096, 0x0D41, 0x1056, 0x0D81, 0x1036, 0x0E03, 0x180E, 0x0E05, 0x140E, |
0x0E09, 0x120E, 0x0E11, 0x110E, 0x0E21, 0x108E, 0x0E41, 0x104E, 0x0E81, 0x102E, 0x0F01, 0x101E, 0x100F, 0x1E01, 0x1017, 0x1D01, 0x101B, 0x1B01, 0x101D, 0x1701, 0x1027, 0x1C81, 0x102B, |
0x1A81, 0x102D, 0x1681, 0x1033, 0x1981, 0x1035, 0x1581, 0x1039, 0x1381, 0x1047, 0x1C41, 0x104B, 0x1A41, 0x104D, 0x1641, 0x1053, 0x1941, 0x1055, 0x1541, 0x1059, 0x1341, 0x1063, 0x18C1, |
0x1065, 0x14C1, 0x1069, 0x12C1, 0x1071, 0x11C1, 0x1087, 0x1C21, 0x108B, 0x1A21, 0x108D, 0x1621, 0x1093, 0x1921, 0x1095, 0x1521, 0x1099, 0x1321, 0x10A3, 0x18A1, 0x10A5, 0x14A1, 0x10A9, |
0x12A1, 0x10B1, 0x11A1, 0x10C3, 0x1861, 0x10C5, 0x1461, 0x10C9, 0x1261, 0x10D1, 0x1161, 0x1107, 0x1C11, 0x110B, 0x1A11, 0x110D, 0x1611, 0x1113, 0x1911, 0x1115, 0x1511, 0x1119, 0x1311, |
0x1123, 0x1891, 0x1125, 0x1491, 0x1129, 0x1291, 0x1131, 0x1191, 0x1143, 0x1851, 0x1145, 0x1451, 0x1149, 0x1251, 0x1183, 0x1831, 0x1185, 0x1431, 0x1189, 0x1231, 0x1207, 0x1C09, 0x120B, |
0x1A09, 0x120D, 0x1609, 0x1213, 0x1909, 0x1215, 0x1509, 0x1219, 0x1309, 0x1223, 0x1889, 0x1225, 0x1489, 0x1229, 0x1289, 0x1243, 0x1849, 0x1245, 0x1449, 0x1283, 0x1829, 0x1285, 0x1429, |
0x1303, 0x1819, 0x1305, 0x1419, 0x1407, 0x1C05, 0x140B, 0x1A05, 0x140D, 0x1605, 0x1413, 0x1905, 0x1415, 0x1505, 0x1423, 0x1885, 0x1425, 0x1485, 0x1443, 0x1845, 0x1483, 0x1825, 0x1503, |
0x1815, 0x1603, 0x180D, 0x1807, 0x1C03, 0x180B, 0x1A03, 0x1813, 0x1903, 0x1823, 0x1883, 0x1843, 0x1445, 0x1249, 0x1151, 0x10E1, 0x0C46, 0x0A4A, 0x0952, 0x08E2, 0x064C, 0x0554, 0x04E4, |
0x0358, 0x02E8, 0x01F0 }; |
/** Appendix D Table II - 2 of 13 characters */ |
private static final int[] APPX_D_II = { 0x0003, 0x1800, 0x0005, 0x1400, 0x0006, 0x0C00, 0x0009, 0x1200, 0x000A, 0x0A00, 0x000C, 0x0600, 0x0011, 0x1100, 0x0012, 0x0900, 0x0014, 0x0500, 0x0018, |
0x0300, 0x0021, 0x1080, 0x0022, 0x0880, 0x0024, 0x0480, 0x0028, 0x0280, 0x0030, 0x0180, 0x0041, 0x1040, 0x0042, 0x0840, 0x0044, 0x0440, 0x0048, 0x0240, 0x0050, 0x0140, 0x0060, 0x00C0, |
0x0081, 0x1020, 0x0082, 0x0820, 0x0084, 0x0420, 0x0088, 0x0220, 0x0090, 0x0120, 0x0101, 0x1010, 0x0102, 0x0810, 0x0104, 0x0410, 0x0108, 0x0210, 0x0201, 0x1008, 0x0202, 0x0808, 0x0204, |
0x0408, 0x0401, 0x1004, 0x0402, 0x0804, 0x0801, 0x1002, 0x1001, 0x0802, 0x0404, 0x0208, 0x0110, 0x00A0 }; |
/** Appendix D Table IV - Bar-to-Character Mapping (reverse lookup) */ |
private static final int[] APPX_D_IV = { 67, 6, 78, 16, 86, 95, 34, 40, 45, 113, 117, 121, 62, 87, 18, 104, 41, 76, 57, 119, 115, 72, 97, 2, 127, 26, 105, 35, 122, 52, 114, 7, 24, 82, 68, 63, 94, |
44, 77, 112, 70, 100, 39, 30, 107, 15, 125, 85, 10, 65, 54, 88, 20, 106, 46, 66, 8, 116, 29, 61, 99, 80, 90, 37, 123, 51, 25, 84, 129, 56, 4, 109, 96, 28, 36, 47, 11, 71, 33, 102, 21, 9, |
17, 49, 124, 79, 64, 91, 42, 69, 53, 60, 14, 1, 27, 103, 126, 75, 89, 50, 120, 19, 32, 110, 92, 111, 130, 59, 31, 12, 81, 43, 55, 5, 74, 22, 101, 128, 58, 118, 48, 108, 38, 98, 93, 23, 83, |
13, 73, 3 }; |
public UspsOneCode() { |
this.default_height = 8; |
this.humanReadableLocation = HumanReadableLocation.NONE; |
this.humanReadableAlignment = HumanReadableAlignment.LEFT; // spec section 2.4.2 |
} |
@Override |
protected void encode() { |
String zip = ""; |
String zip_adder; |
String tracker = ""; |
int i, j; |
final int length = this.content.length(); |
BigInteger accum; |
BigInteger x_reg; |
BigInteger mask; |
int usps_crc; |
final int[] codeword = new int[10]; |
final int[] characters = new int[10]; |
final boolean[] bar_map = new boolean[130]; |
char c; |
if (!this.content.matches("[0-9\u002D]+")) { |
throw new OkapiException("Invalid characters in input data"); |
} |
if (length > 32) { |
throw new OkapiException("Input too long"); |
} |
/* separate the tracking code from the routing code */ |
j = 0; |
for (i = 0; i < length; i++) { |
if (this.content.charAt(i) == '-') { |
j = 1; |
} else { |
if (j == 0) { |
/* reading tracker */ |
tracker += this.content.charAt(i); |
} else { |
/* reading zip code */ |
zip += this.content.charAt(i); |
} |
} |
} |
if (tracker.length() != 20) { |
throw new OkapiException("Invalid length tracking code"); |
} |
if (zip.length() > 11) { |
throw new OkapiException("Invalid ZIP code"); |
} |
/* *** Step 1 - Conversion of Data Fields into Binary Data *** */ |
/* Routing code first */ |
if (zip.length() > 0) { |
x_reg = new BigInteger(zip); |
} else { |
x_reg = new BigInteger("0"); |
} |
/* add weight to routing code */ |
if (zip.length() > 9) { |
zip_adder = "1000100001"; |
} else { |
if (zip.length() > 5) { |
zip_adder = "100001"; |
} else { |
if (zip.length() > 0) { |
zip_adder = "1"; |
} else { |
zip_adder = "0"; |
} |
} |
} |
accum = new BigInteger(zip_adder); |
accum = accum.add(x_reg); |
accum = accum.multiply(BigInteger.valueOf(10)); |
accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(0)))); |
accum = accum.multiply(BigInteger.valueOf(5)); |
accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(1)))); |
for (i = 2; i < tracker.length(); i++) { |
accum = accum.multiply(BigInteger.valueOf(10)); |
accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(i)))); |
} |
/* *** Step 2 - Generation of 11-bit CRC on Binary Data *** */ |
final int[] byte_array = new int[13]; |
for (i = 0; i < byte_array.length; i++) { |
mask = accum.shiftRight(96 - 8 * i); |
mask = mask.and(new BigInteger("255")); |
byte_array[i] = mask.intValue(); |
} |
usps_crc = USPS_MSB_Math_CRC11GenerateFrameCheckSequence(byte_array); |
/* *** Step 3 - Conversion from Binary Data to Codewords *** */ |
/* start with codeword J which is base 636 */ |
x_reg = accum.mod(BigInteger.valueOf(636)); |
codeword[9] = x_reg.intValue(); |
accum = accum.subtract(x_reg); |
accum = accum.divide(BigInteger.valueOf(636)); |
for (i = 8; i >= 0; i--) { |
x_reg = accum.mod(BigInteger.valueOf(1365)); |
codeword[i] = x_reg.intValue(); |
accum = accum.subtract(x_reg); |
accum = accum.divide(BigInteger.valueOf(1365)); |
} |
for (i = 0; i < 9; i++) { |
if (codeword[i] == 1365) { |
codeword[i] = 0; |
} |
} |
/* *** Step 4 - Inserting Additional Information into Codewords *** */ |
codeword[9] = codeword[9] * 2; |
if (usps_crc >= 1024) { |
codeword[0] += 659; |
} |
info("Codewords: "); |
for (i = 0; i < 10; i++) { |
infoSpace(codeword[i]); |
} |
infoLine(); |
/* *** Step 5 - Conversion from Codewords to Characters *** */ |
for (i = 0; i < 10; i++) { |
if (codeword[i] < 1287) { |
characters[i] = APPX_D_I[codeword[i]]; |
} else { |
characters[i] = APPX_D_II[codeword[i] - 1287]; |
} |
} |
for (i = 0; i < 10; i++) { |
if ((usps_crc & 1 << i) != 0) { |
characters[i] = 0x1FFF - characters[i]; |
} |
} |
/* *** Step 6 - Conversion from Characters to the Intelligent Mail Barcode *** */ |
for (i = 0; i < 10; i++) { |
for (j = 0; j < 13; j++) { |
if ((characters[i] & 1 << j) == 0) { |
bar_map[APPX_D_IV[13 * i + j] - 1] = false; |
} else { |
bar_map[APPX_D_IV[13 * i + j] - 1] = true; |
} |
} |
} |
this.readable = formatHumanReadableText(this.content); |
this.pattern = new String[1]; |
this.row_count = 1; |
this.row_height = new int[1]; |
this.row_height[0] = -1; |
this.pattern[0] = ""; |
for (i = 0; i < 65; i++) { |
c = 'T'; |
if (bar_map[i]) { |
c = 'D'; |
} |
if (bar_map[i + 65]) { |
c = 'A'; |
} |
if (bar_map[i] && bar_map[i + 65]) { |
c = 'F'; |
} |
this.pattern[0] += c; |
} |
infoLine("Encoding: " + this.pattern[0]); |
} |
private static int USPS_MSB_Math_CRC11GenerateFrameCheckSequence(final int[] bytes) { |
final int generatorPolynomial = 0x0F35; |
int frameCheckSequence = 0x07FF; |
int data; |
int byteIndex, bit; |
int byteArrayPtr = 0; |
/* Do most significant byte skipping the 2 most significant bits */ |
data = bytes[byteArrayPtr] << 5; |
byteArrayPtr++; |
for (bit = 2; bit < 8; bit++) { |
if (((frameCheckSequence ^ data) & 0x400) != 0) { |
frameCheckSequence = frameCheckSequence << 1 ^ generatorPolynomial; |
} else { |
frameCheckSequence = frameCheckSequence << 1; |
} |
frameCheckSequence &= 0x7FF; |
data <<= 1; |
} |
/* Do rest of the bytes */ |
for (byteIndex = 1; byteIndex < 13; byteIndex++) { |
data = bytes[byteArrayPtr] << 3; |
byteArrayPtr++; |
for (bit = 0; bit < 8; bit++) { |
if (((frameCheckSequence ^ data) & 0x0400) != 0) { |
frameCheckSequence = frameCheckSequence << 1 ^ generatorPolynomial; |
} else { |
frameCheckSequence = frameCheckSequence << 1; |
} |
frameCheckSequence &= 0x7FF; |
data <<= 1; |
} |
} |
return frameCheckSequence; |
} |
/** |
* <p> |
* Formats the barcode content into the correct human-readable format, per section 2.4.3 of the |
* spec: |
* |
* <p> |
* The human-readable information, when required, shall consist of the 20-digit tracking code |
* and the 5-, 9-, or 11-digit routing code, if present. The fields of the tracking code, as |
* defined in 2.1.3, shall be separated with a space added between data fields. When the barcode |
* contains a routing code, the 5-digit ZIP Code, the 4-digit add-on, and the remaining 2 digits |
* shall be separated with a space added between data fields. |
* |
* <p> |
* Appendix F contains a good overview of the different IMb constructs / formats. |
* |
* @param content the content to be formatted |
* @return the formatted content |
*/ |
protected static String formatHumanReadableText(final String content) { |
final StringBuilder hrt = new StringBuilder(50); |
boolean mid9 = false; // 9-digit mailer ID instead of 6-digit mailer ID |
boolean tracing = true; // STID indicates Origin IMb Tracing Services (050, 052) |
boolean pimb = true; // barcode identifier (BI) is 94, indicating pIMb |
boolean mpe5 = false; // if MPE = 5, it's a CFS/RFS variant of pIMb |
int i = 0; |
for (final char c : content.toCharArray()) { |
if (c < '0' || c > '9') { |
continue; |
} |
if (i == 5 && c == '9') { |
mid9 = true; |
} |
if (i == 2 && c != '0' || i == 3 && c != '5' || i == 4 && c != '0' && c != '2') { |
tracing = false; |
} |
if (i == 0 && c != '9' || i == 1 && c != '4') { |
pimb = false; |
} |
if (i == 5 && c == '5') { |
mpe5 = true; |
} |
if (i == 2 // BI -> STID |
|| i == 5 // STID -> ... |
|| i == 6 && pimb || i == 10 && pimb || i == 13 && pimb && !mpe5 || i == 15 && pimb && !mpe5 || i == 11 && !mid9 && !tracing && !pimb || i == 14 && mid9 && !tracing && !pimb |
|| i == 20 // ... -> zip-5 |
|| i == 25 // zip-5 -> zip-4 |
|| i == 29) { // zip-4 -> zip-2 |
hrt.append(' '); |
} |
hrt.append(c); |
i++; |
} |
return hrt.toString().trim(); |
} |
@Override |
protected void plotSymbol() { |
int xBlock, shortHeight, longHeight; |
double x, y, w, h; |
this.rectangles.clear(); |
this.texts.clear(); |
int baseY; |
if (this.humanReadableLocation == TOP) { |
baseY = getTheoreticalHumanReadableHeight(); |
} else { |
baseY = 0; |
} |
x = 0; |
w = this.moduleWidth; |
y = 0; |
h = 0; |
shortHeight = (int) (0.25 * this.default_height); |
longHeight = (int) (0.625 * this.default_height); |
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) { |
switch (this.pattern[0].charAt(xBlock)) { |
case 'A': |
y = baseY; |
h = longHeight; |
break; |
case 'D': |
y = baseY + this.default_height - longHeight; |
h = longHeight; |
break; |
case 'F': |
y = baseY; |
h = this.default_height; |
break; |
case 'T': |
y = baseY + this.default_height - longHeight; |
h = shortHeight; |
break; |
} |
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); |
this.rectangles.add(rect); |
x += 2.43 * w; |
} |
this.symbol_width = (int) Math.ceil((this.pattern[0].length() - 1) * 2.43 * w + w); // final |
// bar |
// doesn't |
// need |
// extra |
// whitespace |
this.symbol_height = this.default_height; |
if (this.humanReadableLocation != NONE && !this.readable.isEmpty()) { |
double baseline; |
if (this.humanReadableLocation == TOP) { |
baseline = this.fontSize; |
} else { |
baseline = this.symbol_height + this.fontSize; |
} |
this.texts.add(new TextBox(0, baseline, this.symbol_width, this.readable, this.humanReadableAlignment)); |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/CodablockF.java |
---|
New file |
0,0 → 1,869 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import java.awt.geom.Rectangle2D; |
import java.nio.charset.StandardCharsets; |
/** |
* <p> |
* Implements Codablock-F according to AIM Europe "Uniform Symbology Specification - Codablock F", |
* 1995. |
* |
* <p> |
* Codablock-F is a multi-row symbology using Code 128 encoding. It can encode any 8-bit ISO 8859-1 |
* (Latin-1) data up to approximately 1000 alpha-numeric characters or 2000 numeric digits in |
* length. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class CodablockF extends Symbol { |
private enum Mode { |
SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB |
} |
private enum CfMode { |
MODEA, MODEB, MODEC |
} |
/* Annex A Table A.1 */ |
private static final String[] C_128_TABLE = { "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231", |
"113222", "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121", |
"111323", "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131", |
"213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", |
"112214", "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212", |
"421112", "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214", |
"211232", "2331112" }; |
private final int[][] blockmatrix = new int[44][62]; |
private int columns_needed; |
private int rows_needed; |
private CfMode final_mode; |
private final CfMode[] subset_selector = new CfMode[44]; |
/** |
* TODO: It doesn't appear that this symbol should support GS1 (it's not in the GS1 spec and |
* Zint doesn't support GS1 with this type of symbology). However, the code below does contain |
* GS1 checks, so we'll mark it as supported for now. It's very possible that the code below |
* which supports GS1 only does so because it was originally copied from the Code 128 source |
* code (just a suspicion, though). |
*/ |
@Override |
protected boolean gs1Supported() { |
return true; |
} |
@Override |
protected void encode() { |
int input_length, i, j, k; |
int min_module_height; |
Mode last_mode, this_mode; |
double estimate_codelength; |
String row_pattern; |
final int[] row_indicator = new int[44]; |
final int[] row_check = new int[44]; |
int k1_sum, k2_sum; |
int k1_check, k2_check; |
this.final_mode = CfMode.MODEA; |
if (!this.content.matches("[\u0000-\u00FF]+")) { |
throw new OkapiException("Invalid characters in input data"); |
} |
this.inputData = toBytes(this.content, StandardCharsets.ISO_8859_1, 0x00); |
input_length = this.inputData.length - 1; |
if (input_length > 5450) { |
throw new OkapiException("Input data too long"); |
} |
/* Make a guess at how many characters will be needed to encode the data */ |
estimate_codelength = 0.0; |
last_mode = Mode.AORB; /* Codablock always starts with Code A */ |
for (i = 0; i < input_length; i++) { |
this_mode = findSubset(this.inputData[i]); |
if (this_mode != last_mode) { |
estimate_codelength += 1.0; |
} |
if (this_mode != Mode.ABORC) { |
estimate_codelength += 1.0; |
} else { |
estimate_codelength += 0.5; |
} |
if (this.inputData[i] > 127) { |
estimate_codelength += 1.0; |
} |
last_mode = this_mode; |
} |
/* Decide symbol size based on the above guess */ |
this.rows_needed = (int) (0.5 + Math.sqrt((estimate_codelength + 2) / 1.45)); |
if (this.rows_needed < 2) { |
this.rows_needed = 2; |
} |
if (this.rows_needed > 44) { |
this.rows_needed = 44; |
} |
this.columns_needed = (int) (estimate_codelength + 2) / this.rows_needed; |
if (this.columns_needed < 4) { |
this.columns_needed = 4; |
} |
if (this.columns_needed > 62) { |
throw new OkapiException("Input data too long"); |
} |
/* Encode the data */ |
data_encode_blockf(); |
/* Add check digits - Annex F */ |
k1_sum = 0; |
k2_sum = 0; |
for (i = 0; i < input_length; i++) { |
if (this.inputData[i] == FNC1) { |
k1_sum += (i + 1) * 29; /* GS */ |
k2_sum += i * 29; |
} else { |
k1_sum += (i + 1) * this.inputData[i]; |
k2_sum += i * this.inputData[i]; |
} |
} |
k1_check = k1_sum % 86; |
k2_check = k2_sum % 86; |
if (this.final_mode == CfMode.MODEA || this.final_mode == CfMode.MODEB) { |
k1_check = k1_check + 64; |
if (k1_check > 95) { |
k1_check -= 96; |
} |
k2_check = k2_check + 64; |
if (k2_check > 95) { |
k2_check -= 96; |
} |
} |
this.blockmatrix[this.rows_needed - 1][this.columns_needed - 2] = k1_check; |
this.blockmatrix[this.rows_needed - 1][this.columns_needed - 1] = k2_check; |
/* Calculate row height (4.6.1.a) */ |
min_module_height = (int) (0.55 * (this.columns_needed + 3)) + 3; |
if (min_module_height < 8) { |
min_module_height = 8; |
} |
/* Encode the Row Indicator in the First Row of the Symbol - Table D2 */ |
if (this.subset_selector[0] == CfMode.MODEC) { |
/* Code C */ |
row_indicator[0] = this.rows_needed - 2; |
} else { |
/* Code A or B */ |
row_indicator[0] = this.rows_needed + 62; |
if (row_indicator[0] > 95) { |
row_indicator[0] -= 95; |
} |
} |
/* Encode the Row Indicator in the Second and Subsequent Rows of the Symbol - Table D3 */ |
for (i = 1; i < this.rows_needed; i++) { |
/* Note that the second row is row number 1 because counting starts from 0 */ |
if (this.subset_selector[i] == CfMode.MODEC) { |
/* Code C */ |
row_indicator[i] = i + 42; |
} else { |
/* Code A or B */ |
if (i < 6) { |
row_indicator[i] = i + 10; |
} else { |
row_indicator[i] = i + 20; |
} |
} |
} |
/* Calculate row check digits - Annex E */ |
for (i = 0; i < this.rows_needed; i++) { |
k = 103; |
switch (this.subset_selector[i]) { |
case MODEA: |
k += 98; |
break; |
case MODEB: |
k += 100; |
break; |
case MODEC: |
k += 99; |
break; |
} |
k += 2 * row_indicator[i]; |
for (j = 0; j < this.columns_needed; j++) { |
k += (j + 3) * this.blockmatrix[i][j]; |
} |
row_check[i] = k % 103; |
} |
this.readable = ""; |
this.row_count = this.rows_needed; |
this.pattern = new String[this.row_count]; |
this.row_height = new int[this.row_count]; |
infoLine("Grid Size: " + this.columns_needed + " X " + this.rows_needed); |
infoLine("K1 Check Digit: " + k1_check); |
infoLine("K2 Check Digit: " + k2_check); |
/* Resolve the data into patterns and place in symbol structure */ |
info("Encoding: "); |
for (i = 0; i < this.rows_needed; i++) { |
row_pattern = ""; |
/* Start character */ |
row_pattern += C_128_TABLE[103]; /* Always Start A */ |
switch (this.subset_selector[i]) { |
case MODEA: |
row_pattern += C_128_TABLE[98]; |
info("MODEA "); |
break; |
case MODEB: |
row_pattern += C_128_TABLE[100]; |
info("MODEB "); |
break; |
case MODEC: |
row_pattern += C_128_TABLE[99]; |
info("MODEC "); |
break; |
} |
row_pattern += C_128_TABLE[row_indicator[i]]; |
infoSpace(row_indicator[i]); |
for (j = 0; j < this.columns_needed; j++) { |
row_pattern += C_128_TABLE[this.blockmatrix[i][j]]; |
infoSpace(this.blockmatrix[i][j]); |
} |
row_pattern += C_128_TABLE[row_check[i]]; |
info("(" + row_check[i] + ") "); |
/* Stop character */ |
row_pattern += C_128_TABLE[106]; |
/* Write the information into the symbol */ |
this.pattern[i] = row_pattern; |
this.row_height[i] = 15; |
} |
infoLine(); |
this.symbol_height = this.rows_needed * 15; |
} |
private Mode findSubset(final int letter) { |
Mode mode; |
if (letter == FNC1) { |
mode = Mode.AORB; |
} else if (letter <= 31) { |
mode = Mode.SHIFTA; |
} else if (letter >= 48 && letter <= 57) { |
mode = Mode.ABORC; |
} else if (letter <= 95) { |
mode = Mode.AORB; |
} else if (letter <= 127) { |
mode = Mode.SHIFTB; |
} else if (letter <= 159) { |
mode = Mode.SHIFTA; |
} else if (letter <= 223) { |
mode = Mode.AORB; |
} else { |
mode = Mode.SHIFTB; |
} |
return mode; |
} |
private void data_encode_blockf() { |
int i, j, input_position, current_row; |
int column_position, c; |
CfMode current_mode; |
boolean done, exit_status; |
final int input_length = this.inputData.length - 1; |
exit_status = false; |
current_row = 0; |
current_mode = CfMode.MODEA; |
column_position = 0; |
input_position = 0; |
c = 0; |
do { |
done = false; |
/* |
* 'done' ensures that the instructions are followed in the correct order for each input |
* character |
*/ |
if (column_position == 0) { |
/* The Beginning of a row */ |
c = this.columns_needed; |
current_mode = character_subset_select(input_position); |
this.subset_selector[current_row] = current_mode; |
if (current_row == 0 && this.inputDataType == DataType.GS1) { |
/* Section 4.4.7.1 */ |
this.blockmatrix[current_row][column_position] = 102; /* FNC1 */ |
column_position++; |
c--; |
} |
} |
if (this.inputData[input_position] == FNC1) { |
this.blockmatrix[current_row][column_position] = 102; /* FNC1 */ |
column_position++; |
c--; |
input_position++; |
done = true; |
} |
if (!done) { |
if (c <= 2) { |
/* Annex B section 1 rule 1 */ |
/* |
* Ensure that there is sufficient encodation capacity to continue (using the |
* rules of Annex B.2). |
*/ |
switch (current_mode) { |
case MODEA: /* Table B1 applies */ |
if (findSubset(this.inputData[input_position]) == Mode.ABORC) { |
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]); |
column_position++; |
c--; |
input_position++; |
done = true; |
} |
if (findSubset(this.inputData[input_position]) == Mode.SHIFTB && c == 1) { |
/* Needs two symbols */ |
this.blockmatrix[current_row][column_position] = 100; /* Code B */ |
column_position++; |
c--; |
done = true; |
} |
if (this.inputData[input_position] >= 244 && !done) { |
/* Needs three symbols */ |
this.blockmatrix[current_row][column_position] = 100; /* Code B */ |
column_position++; |
c--; |
if (c == 1) { |
this.blockmatrix[current_row][column_position] = 101; /* Code A */ |
column_position++; |
c--; |
} |
done = true; |
} |
if (this.inputData[input_position] >= 128 && !done && c == 1) { |
/* Needs two symbols */ |
this.blockmatrix[current_row][column_position] = 100; /* Code B */ |
column_position++; |
c--; |
done = true; |
} |
break; |
case MODEB: /* Table B2 applies */ |
if (findSubset(this.inputData[input_position]) == Mode.ABORC) { |
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]); |
column_position++; |
c--; |
input_position++; |
done = true; |
} |
if (findSubset(this.inputData[input_position]) == Mode.SHIFTA && c == 1) { |
/* Needs two symbols */ |
this.blockmatrix[current_row][column_position] = 101; /* Code A */ |
column_position++; |
c--; |
done = true; |
} |
if (this.inputData[input_position] >= 128 && this.inputData[input_position] <= 159 && !done) { |
/* Needs three symbols */ |
this.blockmatrix[current_row][column_position] = 101; /* Code A */ |
column_position++; |
c--; |
if (c == 1) { |
this.blockmatrix[current_row][column_position] = 100; /* Code B */ |
column_position++; |
c--; |
} |
done = true; |
} |
if (this.inputData[input_position] >= 160 && !done && c == 1) { |
/* Needs two symbols */ |
this.blockmatrix[current_row][column_position] = 101; /* Code A */ |
column_position++; |
c--; |
done = true; |
} |
break; |
case MODEC: /* Table B3 applies */ |
if (findSubset(this.inputData[input_position]) != Mode.ABORC && c == 1) { |
/* Needs two symbols */ |
this.blockmatrix[current_row][column_position] = 101; /* Code A */ |
column_position++; |
c--; |
done = true; |
} |
if (findSubset(this.inputData[input_position]) == Mode.ABORC && findSubset(this.inputData[input_position + 1]) != Mode.ABORC && c == 1) { |
/* Needs two symbols */ |
this.blockmatrix[current_row][column_position] = 101; /* Code A */ |
column_position++; |
c--; |
done = true; |
} |
if (this.inputData[input_position] >= 128) { |
/* Needs three symbols */ |
this.blockmatrix[current_row][column_position] = 101; /* Code A */ |
column_position++; |
c--; |
if (c == 1) { |
this.blockmatrix[current_row][column_position] = 100; /* Code B */ |
column_position++; |
c--; |
} |
} |
break; |
} |
} |
} |
if (!done) { |
if ((findSubset(this.inputData[input_position]) == Mode.AORB || findSubset(this.inputData[input_position]) == Mode.SHIFTA) && current_mode == CfMode.MODEA) { |
/* Annex B section 1 rule 2 */ |
/* |
* If in Code Subset A and the next data character can be encoded in Subset A |
* encode the next character. |
*/ |
if (this.inputData[input_position] >= 128) { |
/* Extended ASCII character */ |
this.blockmatrix[current_row][column_position] = 101; /* FNC4 */ |
column_position++; |
c--; |
} |
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]); |
column_position++; |
c--; |
input_position++; |
done = true; |
} |
} |
if (!done) { |
if ((findSubset(this.inputData[input_position]) == Mode.AORB || findSubset(this.inputData[input_position]) == Mode.SHIFTB) && current_mode == CfMode.MODEB) { |
/* Annex B section 1 rule 3 */ |
/* |
* If in Code Subset B and the next data character can be encoded in subset B, |
* encode the next character. |
*/ |
if (this.inputData[input_position] >= 128) { |
/* Extended ASCII character */ |
this.blockmatrix[current_row][column_position] = 100; /* FNC4 */ |
column_position++; |
c--; |
} |
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]); |
column_position++; |
c--; |
input_position++; |
done = true; |
} |
} |
if (!done) { |
if (findSubset(this.inputData[input_position]) == Mode.ABORC && findSubset(this.inputData[input_position + 1]) == Mode.ABORC && current_mode == CfMode.MODEC) { |
/* Annex B section 1 rule 4 */ |
/* If in Code Subset C and the next data are 2 digits, encode them. */ |
this.blockmatrix[current_row][column_position] = (this.inputData[input_position] - '0') * 10 + this.inputData[input_position + 1] - '0'; |
column_position++; |
c--; |
input_position += 2; |
done = true; |
} |
} |
if (!done) { |
if ((current_mode == CfMode.MODEA || current_mode == CfMode.MODEB) && (findSubset(this.inputData[input_position]) == Mode.ABORC || this.inputData[input_position] == FNC1)) { |
// Count the number of numeric digits |
// If 4 or more numeric data characters occur together when in subsets A or B: |
// a. If there is an even number of numeric data characters, insert a Code C |
// character before the |
// first numeric digit to change to subset C. |
// b. If there is an odd number of numeric data characters, insert a Code Set C |
// character immediately |
// after the first numeric digit to change to subset C. |
i = 0; |
j = 0; |
do { |
i++; |
if (this.inputData[input_position + j] == FNC1) { |
i++; |
} |
j++; |
} while (findSubset(this.inputData[input_position + j]) == Mode.ABORC || this.inputData[input_position + j] == FNC1); |
i--; |
if (i >= 4) { |
/* Annex B section 1 rule 5 */ |
if (i % 2 == 1) { |
/* Annex B section 1 rule 5a */ |
this.blockmatrix[current_row][column_position] = 99; /* Code C */ |
column_position++; |
c--; |
this.blockmatrix[current_row][column_position] = (this.inputData[input_position] - '0') * 10 + this.inputData[input_position + 1] - '0'; |
column_position++; |
c--; |
input_position += 2; |
current_mode = CfMode.MODEC; |
} else { |
/* Annex B section 1 rule 5b */ |
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]); |
column_position++; |
c--; |
input_position++; |
} |
done = true; |
} else { |
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]); |
column_position++; |
c--; |
input_position++; |
done = true; |
} |
} |
} |
if (!done) { |
if (current_mode == CfMode.MODEB && findSubset(this.inputData[input_position]) == Mode.SHIFTA) { |
/* Annex B section 1 rule 6 */ |
/* |
* When in subset B and an ASCII control character occurs in the data: a. If |
* there is a lower case character immediately following the control character, |
* insert a Shift character before the control character. b. Otherwise, insert a |
* Code A character before the control character to change to subset A. |
*/ |
if (this.inputData[input_position + 1] >= 96 && this.inputData[input_position + 1] <= 127) { |
/* Annex B section 1 rule 6a */ |
this.blockmatrix[current_row][column_position] = 98; /* Shift */ |
column_position++; |
c--; |
if (this.inputData[input_position] >= 128) { |
/* Extended ASCII character */ |
this.blockmatrix[current_row][column_position] = 100; /* FNC4 */ |
column_position++; |
c--; |
} |
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]); |
column_position++; |
c--; |
input_position++; |
} else { |
/* Annex B section 1 rule 6b */ |
this.blockmatrix[current_row][column_position] = 101; /* Code A */ |
column_position++; |
c--; |
if (this.inputData[input_position] >= 128) { |
/* Extended ASCII character */ |
this.blockmatrix[current_row][column_position] = 100; /* FNC4 */ |
column_position++; |
c--; |
} |
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]); |
column_position++; |
c--; |
input_position++; |
current_mode = CfMode.MODEA; |
} |
done = true; |
} |
} |
if (!done) { |
if (current_mode == CfMode.MODEA && findSubset(this.inputData[input_position]) == Mode.SHIFTB) { |
/* Annex B section 1 rule 7 */ |
/* |
* When in subset A and a lower case character occurs in the data: a. If |
* following that character, a control character occurs in the data before the |
* occurrence of another lower case character, insert a Shift character before |
* the lower case character. b. Otherwise, insert a Code B character before the |
* lower case character to change to subset B. |
*/ |
if (findSubset(this.inputData[input_position + 1]) == Mode.SHIFTA && findSubset(this.inputData[input_position + 2]) == Mode.SHIFTB) { |
/* Annex B section 1 rule 7a */ |
this.blockmatrix[current_row][column_position] = 98; /* Shift */ |
column_position++; |
c--; |
if (this.inputData[input_position] >= 128) { |
/* Extended ASCII character */ |
this.blockmatrix[current_row][column_position] = 101; /* FNC4 */ |
column_position++; |
c--; |
} |
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]); |
column_position++; |
c--; |
input_position++; |
} else { |
/* Annex B section 1 rule 7b */ |
this.blockmatrix[current_row][column_position] = 100; /* Code B */ |
column_position++; |
c--; |
if (this.inputData[input_position] >= 128) { |
/* Extended ASCII character */ |
this.blockmatrix[current_row][column_position] = 101; /* FNC4 */ |
column_position++; |
c--; |
} |
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]); |
column_position++; |
c--; |
input_position++; |
current_mode = CfMode.MODEB; |
} |
done = true; |
} |
} |
if (!done) { |
if (current_mode == CfMode.MODEC && (findSubset(this.inputData[input_position]) != Mode.ABORC || findSubset(this.inputData[input_position + 1]) != Mode.ABORC)) { |
/* Annex B section 1 rule 8 */ |
/* |
* When in subset C and a non-numeric character (or a single digit) occurs in |
* the data, insert a Code A or Code B character before that character, |
* following rules 8a and 8b to determine between code subsets A and B. a. If an |
* ASCII control character (eg NUL) occurs in the data before any lower case |
* character, use Code A. b. Otherwise use Code B. |
*/ |
if (findSubset(this.inputData[input_position]) == Mode.SHIFTA) { |
/* Annex B section 1 rule 8a */ |
this.blockmatrix[current_row][column_position] = 101; /* Code A */ |
column_position++; |
c--; |
if (this.inputData[input_position] >= 128) { |
/* Extended ASCII character */ |
this.blockmatrix[current_row][column_position] = 101; /* FNC4 */ |
column_position++; |
c--; |
} |
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]); |
column_position++; |
c--; |
input_position++; |
current_mode = CfMode.MODEA; |
} else { |
/* Annex B section 1 rule 8b */ |
this.blockmatrix[current_row][column_position] = 100; /* Code B */ |
column_position++; |
c--; |
if (this.inputData[input_position] >= 128) { |
/* Extended ASCII character */ |
this.blockmatrix[current_row][column_position] = 100; /* FNC4 */ |
column_position++; |
c--; |
} |
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]); |
column_position++; |
c--; |
input_position++; |
current_mode = CfMode.MODEB; |
} |
done = true; |
} |
} |
if (input_position == input_length) { |
/* End of data - Annex B rule 5a */ |
if (c == 1) { |
if (current_mode == CfMode.MODEA) { |
this.blockmatrix[current_row][column_position] = 100; /* Code B */ |
current_mode = CfMode.MODEB; |
} else { |
this.blockmatrix[current_row][column_position] = 101; /* Code A */ |
current_mode = CfMode.MODEA; |
} |
column_position++; |
c--; |
} |
if (c == 0) { |
/* Another row is needed */ |
column_position = 0; |
c = this.columns_needed; |
current_row++; |
this.subset_selector[current_row] = CfMode.MODEA; |
current_mode = CfMode.MODEA; |
} |
if (c > 2) { |
/* Fill up the last row */ |
do { |
if (current_mode == CfMode.MODEA) { |
this.blockmatrix[current_row][column_position] = 100; /* Code B */ |
current_mode = CfMode.MODEB; |
} else { |
this.blockmatrix[current_row][column_position] = 101; /* Code A */ |
current_mode = CfMode.MODEA; |
} |
column_position++; |
c--; |
} while (c > 2); |
} |
/* If (c == 2) { do nothing } */ |
exit_status = true; |
this.final_mode = current_mode; |
} else { |
if (c <= 0) { |
/* Start new row - Annex B rule 5b */ |
column_position = 0; |
current_row++; |
if (current_row > 43) { |
throw new OkapiException("Too many rows."); |
} |
} |
} |
} while (!exit_status); |
if (current_row == 0) { |
/* fill up the first row */ |
for (c = column_position; c <= this.columns_needed; c++) { |
if (current_mode == CfMode.MODEA) { |
this.blockmatrix[current_row][c] = 100; /* Code B */ |
current_mode = CfMode.MODEB; |
} else { |
this.blockmatrix[current_row][c] = 101; /* Code A */ |
current_mode = CfMode.MODEA; |
} |
} |
current_row++; |
/* add a second row */ |
this.subset_selector[current_row] = CfMode.MODEA; |
current_mode = CfMode.MODEA; |
for (c = 0; c <= this.columns_needed - 2; c++) { |
if (current_mode == CfMode.MODEA) { |
this.blockmatrix[current_row][c] = 100; /* Code B */ |
current_mode = CfMode.MODEB; |
} else { |
this.blockmatrix[current_row][c] = 101; /* Code A */ |
current_mode = CfMode.MODEA; |
} |
} |
} |
this.rows_needed = current_row + 1; |
} |
private CfMode character_subset_select(final int input_position) { |
/* Section 4.5.2 - Determining the Character Subset Selector in a Row */ |
if (this.inputData[input_position] >= '0' && this.inputData[input_position] <= '9') { |
/* Rule 1 */ |
return CfMode.MODEC; |
} |
if (this.inputData[input_position] >= 128 && this.inputData[input_position] <= 160) { |
/* Rule 2 (i) */ |
return CfMode.MODEA; |
} |
if (this.inputData[input_position] >= 0 && this.inputData[input_position] <= 31) { |
/* Rule 3 */ |
return CfMode.MODEA; |
} |
/* Rule 4 */ |
return CfMode.MODEB; |
} |
private int a3_convert(final int source) { |
/* Annex A section 3 */ |
if (source < 32) { |
return source + 64; |
} |
if (source >= 32 && source <= 127) { |
return source - 32; |
} |
if (source >= 128 && source <= 159) { |
return source - 128 + 64; |
} |
/* if source >= 160 */ |
return source - 128 - 32; |
} |
@Override |
protected void plotSymbol() { |
int xBlock, yBlock; |
int x, y, w, h; |
boolean black; |
this.rectangles.clear(); |
y = 1; |
h = 1; |
for (yBlock = 0; yBlock < this.row_count; yBlock++) { |
black = true; |
x = 0; |
for (xBlock = 0; xBlock < this.pattern[yBlock].length(); xBlock++) { |
if (black) { |
black = false; |
w = this.pattern[yBlock].charAt(xBlock) - '0'; |
if (this.row_height[yBlock] == -1) { |
h = this.default_height; |
} else { |
h = this.row_height[yBlock]; |
} |
if (w != 0 && h != 0) { |
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); |
this.rectangles.add(rect); |
} |
if (x + w > this.symbol_width) { |
this.symbol_width = x + w; |
} |
} else { |
black = true; |
} |
x += this.pattern[yBlock].charAt(xBlock) - '0'; |
} |
y += h; |
if (y > this.symbol_height) { |
this.symbol_height = y; |
} |
/* Add bars between rows */ |
if (yBlock != this.row_count - 1) { |
final Rectangle2D.Double rect = new Rectangle2D.Double(11, y - 1, this.symbol_width - 24, 2); |
this.rectangles.add(rect); |
} |
} |
/* Add top and bottom binding bars */ |
final Rectangle2D.Double top = new Rectangle2D.Double(0, 0, this.symbol_width, 2); |
this.rectangles.add(top); |
final Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, this.symbol_width, 2); |
this.rectangles.add(bottom); |
this.symbol_height += 1; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Hexagon.java |
---|
New file |
0,0 → 1,41 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* Calculate a set of points to make a hexagon |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Hexagon { |
private static final double INK_SPREAD = 1.25; |
private static final double[] OFFSET_X = { 0.0, 0.86, 0.86, 0.0, -0.86, -0.86 }; |
private static final double[] OFFSET_Y = { 1.0, 0.5, -0.5, -1.0, -0.5, 0.5 }; |
public final double centreX; |
public final double centreY; |
public final double[] pointX = new double[6]; |
public final double[] pointY = new double[6]; |
public Hexagon(final double centreX, final double centreY) { |
this.centreX = centreX; |
this.centreY = centreY; |
for (int i = 0; i < 6; i++) { |
this.pointX[i] = centreX + OFFSET_X[i] * INK_SPREAD; |
this.pointY[i] = centreY + OFFSET_Y[i] * INK_SPREAD; |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code16k.java |
---|
New file |
0,0 → 1,779 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import java.awt.geom.Rectangle2D; |
import java.nio.charset.StandardCharsets; |
/** |
* <p> |
* Implements Code 16K symbology according to BS EN 12323:2005. |
* |
* <p> |
* Encodes using a stacked symbology based on Code 128. Supports encoding of any 8-bit ISO 8859-1 |
* (Latin-1) data with a maximum data capacity of 77 alpha-numeric characters or 154 numerical |
* digits. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Code16k extends Symbol { |
private enum Mode { |
NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB |
} |
/* EN 12323 Table 1 - "Code 16K" character encodations */ |
private static final String[] C16K_TABLE = { "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231", |
"113222", "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121", |
"111323", "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131", |
"213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221", |
"112214", "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212", |
"421112", "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214", |
"211232", "211133" }; |
/* EN 12323 Table 3 and Table 4 - Start patterns and stop patterns */ |
private static final String[] C16K_START_STOP = { "3211", "2221", "2122", "1411", "1132", "1231", "1114", "3112" }; |
/* EN 12323 Table 5 - Start and stop values defining row numbers */ |
private static final int[] C16K_START_VALUES = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 }; |
private static final int[] C16K_STOP_VALUES = { 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 0, 1, 2, 3 }; |
private final Mode[] block_mode = new Mode[170]; /* RENAME block_mode */ |
private final int[] block_length = new int[170]; /* RENAME block_length */ |
private int block_count; |
@Override |
protected boolean gs1Supported() { |
return true; |
} |
@Override |
protected void encode() { |
// TODO: is it possible to share any of this code with Code128, which is more up to date? |
String width_pattern; |
int current_row, rows_needed, first_check, second_check; |
int indexchaine, pads_needed; |
char[] set, fset; |
Mode mode; |
char last_set, current_set; |
int i, j, k, m, read; |
int[] values; |
int bar_characters; |
double glyph_count; |
int first_sum, second_sum; |
int input_length; |
int c_count; |
boolean f_state; |
if (!this.content.matches("[\u0000-\u00FF]+")) { |
throw new OkapiException("Invalid characters in input data"); |
} |
this.inputData = toBytes(this.content, StandardCharsets.ISO_8859_1); |
input_length = this.inputData.length; |
bar_characters = 0; |
set = new char[160]; |
fset = new char[160]; |
values = new int[160]; |
if (input_length > 157) { |
throw new OkapiException("Input too long"); |
} |
/* Detect extended ASCII characters */ |
for (i = 0; i < input_length; i++) { |
if (this.inputData[i] >= 128) { |
fset[i] = 'f'; |
} else { |
fset[i] = ' '; |
} |
} |
/* Decide when to latch to extended mode */ |
for (i = 0; i < input_length; i++) { |
j = 0; |
if (fset[i] == 'f') { |
do { |
j++; |
} while (fset[i + j] == 'f'); |
if (j >= 5 || j >= 3 && i + j == input_length - 1) { |
for (k = 0; k <= j; k++) { |
fset[i + k] = 'F'; |
} |
} |
} |
} |
/* Decide if it is worth reverting to 646 encodation for a few characters */ |
if (input_length > 1) { |
for (i = 1; i < input_length; i++) { |
if (fset[i - 1] == 'F' && fset[i] == ' ') { |
/* Detected a change from 8859-1 to 646 - count how long for */ |
for (j = 0; fset[i + j] == ' ' && i + j < input_length; j++) { |
; |
} |
if (j < 5 || j < 3 && i + j == input_length - 1) { |
/* Change to shifting back rather than latching back */ |
for (k = 0; k < j; k++) { |
fset[i + k] = 'n'; |
} |
} |
} |
} |
} |
/* Detect mode A, B and C characters */ |
this.block_count = 0; |
indexchaine = 0; |
mode = findSubset(this.inputData[indexchaine]); |
if (this.inputData[indexchaine] == FNC1) { |
mode = Mode.ABORC; |
} /* FNC1 */ |
for (i = 0; i < 160; i++) { |
this.block_length[i] = 0; |
} |
do { |
this.block_mode[this.block_count] = mode; |
while (this.block_mode[this.block_count] == mode && indexchaine < input_length) { |
this.block_length[this.block_count]++; |
indexchaine++; |
if (indexchaine < input_length) { |
mode = findSubset(this.inputData[indexchaine]); |
if (this.inputData[indexchaine] == FNC1) { |
mode = Mode.ABORC; |
} /* FNC1 */ |
} |
} |
this.block_count++; |
} while (indexchaine < input_length); |
reduceSubsetChanges(this.block_count); |
/* Put set data into set[] */ |
read = 0; |
for (i = 0; i < this.block_count; i++) { |
for (j = 0; j < this.block_length[i]; j++) { |
switch (this.block_mode[i]) { |
case SHIFTA: |
set[read] = 'a'; |
break; |
case LATCHA: |
set[read] = 'A'; |
break; |
case SHIFTB: |
set[read] = 'b'; |
break; |
case LATCHB: |
set[read] = 'B'; |
break; |
case LATCHC: |
set[read] = 'C'; |
break; |
} |
read++; |
} |
} |
/* Adjust for strings which start with shift characters - make them latch instead */ |
if (set[0] == 'a') { |
i = 0; |
do { |
set[i] = 'A'; |
i++; |
} while (set[i] == 'a'); |
} |
if (set[0] == 'b') { |
i = 0; |
do { |
set[i] = 'B'; |
i++; |
} while (set[i] == 'b'); |
} |
/* Watch out for odd-length Mode C blocks */ |
c_count = 0; |
for (i = 0; i < read; i++) { |
if (set[i] == 'C') { |
if (this.inputData[i] == FNC1) { |
if ((c_count & 1) != 0) { |
if (i - c_count != 0) { |
set[i - c_count] = 'B'; |
} else { |
set[i - 1] = 'B'; |
} |
} |
c_count = 0; |
} else { |
c_count++; |
} |
} else { |
if ((c_count & 1) != 0) { |
if (i - c_count != 0) { |
set[i - c_count] = 'B'; |
} else { |
set[i - 1] = 'B'; |
} |
} |
c_count = 0; |
} |
} |
if ((c_count & 1) != 0) { |
if (i - c_count != 0) { |
set[i - c_count] = 'B'; |
} else { |
set[i - 1] = 'B'; |
} |
} |
for (i = 1; i < read - 1; i++) { |
if (set[i] == 'C' && set[i - 1] == 'B' && set[i + 1] == 'B') { |
set[i] = 'B'; |
} |
} |
/* Make sure the data will fit in the symbol */ |
last_set = ' '; |
glyph_count = 0.0; |
for (i = 0; i < input_length; i++) { |
if (set[i] == 'a' || set[i] == 'b') { |
glyph_count = glyph_count + 1.0; |
} |
if (fset[i] == 'f' || fset[i] == 'n') { |
glyph_count = glyph_count + 1.0; |
} |
if (set[i] == 'A' || set[i] == 'B' || set[i] == 'C') { |
if (set[i] != last_set) { |
last_set = set[i]; |
glyph_count = glyph_count + 1.0; |
} |
} |
if (i == 0) { |
if (set[i] == 'B' && set[1] == 'C') { |
glyph_count = glyph_count - 1.0; |
} |
if (set[i] == 'B' && set[1] == 'B' && set[2] == 'C') { |
glyph_count = glyph_count - 1.0; |
} |
if (fset[i] == 'F') { |
glyph_count = glyph_count + 2.0; |
} |
} else { |
if (fset[i] == 'F' && fset[i - 1] != 'F') { |
glyph_count = glyph_count + 2.0; |
} |
if (fset[i] != 'F' && fset[i - 1] == 'F') { |
glyph_count = glyph_count + 2.0; |
} |
} |
if (set[i] == 'C' && this.inputData[i] != FNC1) { |
glyph_count = glyph_count + 0.5; |
} else { |
glyph_count = glyph_count + 1.0; |
} |
} |
if (this.inputDataType == DataType.GS1 && set[0] != 'A') { |
/* FNC1 can be integrated with mode character */ |
glyph_count--; |
} |
if (glyph_count > 77.0) { |
throw new OkapiException("Input too long"); |
} |
/* Calculate how tall the symbol will be */ |
glyph_count = glyph_count + 2.0; |
i = (int) glyph_count; |
rows_needed = i / 5; |
if (i % 5 > 0) { |
rows_needed++; |
} |
if (rows_needed == 1) { |
rows_needed = 2; |
} |
/* start with the mode character - Table 2 */ |
m = 0; |
switch (set[0]) { |
case 'A': |
m = 0; |
break; |
case 'B': |
m = 1; |
break; |
case 'C': |
m = 2; |
break; |
} |
if (this.readerInit) { |
if (m == 2) { |
m = 5; |
} |
if (this.inputDataType == DataType.GS1) { |
throw new OkapiException("Cannot use both GS1 mode and Reader Initialisation"); |
} else { |
if (set[0] == 'B' && set[1] == 'C') { |
m = 6; |
} |
} |
values[bar_characters] = 7 * (rows_needed - 2) + m; /* see 4.3.4.2 */ |
values[bar_characters + 1] = 96; /* FNC3 */ |
bar_characters += 2; |
} else { |
if (this.inputDataType == DataType.GS1) { |
/* Integrate FNC1 */ |
switch (set[0]) { |
case 'B': |
m = 3; |
break; |
case 'C': |
m = 4; |
break; |
} |
} else { |
if (set[0] == 'B' && set[1] == 'C') { |
m = 5; |
} |
if (set[0] == 'B' && set[1] == 'B' && set[2] == 'C') { |
m = 6; |
} |
} |
} |
values[bar_characters] = 7 * (rows_needed - 2) + m; /* see 4.3.4.2 */ |
bar_characters++; |
// } |
current_set = set[0]; |
f_state = false; |
/* |
* f_state remembers if we are in Extended ASCII mode (value 1) or in ISO/IEC 646 mode |
* (value 0) |
*/ |
if (fset[0] == 'F') { |
switch (current_set) { |
case 'A': |
values[bar_characters] = 101; |
values[bar_characters + 1] = 101; |
break; |
case 'B': |
values[bar_characters] = 100; |
values[bar_characters + 1] = 100; |
break; |
} |
bar_characters += 2; |
f_state = true; |
} |
read = 0; |
/* Encode the data */ |
do { |
if (read != 0 && set[read] != set[read - 1]) { /* Latch different code set */ |
switch (set[read]) { |
case 'A': |
values[bar_characters] = 101; |
bar_characters++; |
current_set = 'A'; |
break; |
case 'B': |
values[bar_characters] = 100; |
bar_characters++; |
current_set = 'B'; |
break; |
case 'C': |
if (!(read == 1 && set[0] == 'B')) { /* Not Mode C/Shift B */ |
if (!(read == 2 && set[0] == 'B' && set[1] == 'B')) { |
/* Not Mode C/Double Shift B */ |
values[bar_characters] = 99; |
bar_characters++; |
} |
} |
current_set = 'C'; |
break; |
} |
} |
if (read != 0) { |
if (fset[read] == 'F' && !f_state) { |
/* Latch beginning of extended mode */ |
switch (current_set) { |
case 'A': |
values[bar_characters] = 101; |
values[bar_characters + 1] = 101; |
break; |
case 'B': |
values[bar_characters] = 100; |
values[bar_characters + 1] = 100; |
break; |
} |
bar_characters += 2; |
f_state = true; |
} |
if (fset[read] == ' ' && f_state) { |
/* Latch end of extended mode */ |
switch (current_set) { |
case 'A': |
values[bar_characters] = 101; |
values[bar_characters + 1] = 101; |
break; |
case 'B': |
values[bar_characters] = 100; |
values[bar_characters + 1] = 100; |
break; |
} |
bar_characters += 2; |
f_state = false; |
} |
} |
if (fset[i] == 'f' || fset[i] == 'n') { |
/* Shift extended mode */ |
switch (current_set) { |
case 'A': |
values[bar_characters] = 101; /* FNC 4 */ |
break; |
case 'B': |
values[bar_characters] = 100; /* FNC 4 */ |
break; |
} |
bar_characters++; |
} |
if (set[i] == 'a' || set[i] == 'b') { |
/* Insert shift character */ |
values[bar_characters] = 98; |
bar_characters++; |
} |
if (this.inputData[read] != FNC1) { |
switch (set[read]) { /* Encode data characters */ |
case 'A': |
case 'a': |
getValueSubsetA(this.inputData[read], values, bar_characters); |
bar_characters++; |
read++; |
break; |
case 'B': |
case 'b': |
getValueSubsetB(this.inputData[read], values, bar_characters); |
bar_characters++; |
read++; |
break; |
case 'C': |
getValueSubsetC(this.inputData[read], this.inputData[read + 1], values, bar_characters); |
bar_characters++; |
read += 2; |
break; |
} |
} else { |
values[bar_characters] = 102; |
bar_characters++; |
read++; |
} |
} while (read < input_length); |
pads_needed = 5 - (bar_characters + 2) % 5; |
if (pads_needed == 5) { |
pads_needed = 0; |
} |
if (bar_characters + pads_needed < 8) { |
pads_needed += 8 - (bar_characters + pads_needed); |
} |
for (i = 0; i < pads_needed; i++) { |
values[bar_characters] = 106; |
bar_characters++; |
} |
/* Calculate check digits */ |
first_sum = 0; |
second_sum = 0; |
for (i = 0; i < bar_characters; i++) { |
first_sum += (i + 2) * values[i]; |
second_sum += (i + 1) * values[i]; |
} |
first_check = first_sum % 107; |
second_sum += first_check * (bar_characters + 1); |
second_check = second_sum % 107; |
values[bar_characters] = first_check; |
values[bar_characters + 1] = second_check; |
bar_characters += 2; |
this.readable = ""; |
this.pattern = new String[rows_needed]; |
this.row_count = rows_needed; |
this.row_height = new int[rows_needed]; |
infoLine("Symbol Rows: " + rows_needed); |
infoLine("First Check Digit: " + first_check); |
infoLine("Second Check Digit: " + second_check); |
info("Codewords: "); |
for (current_row = 0; current_row < rows_needed; current_row++) { |
width_pattern = ""; |
width_pattern += C16K_START_STOP[C16K_START_VALUES[current_row]]; |
width_pattern += "1"; |
for (i = 0; i < 5; i++) { |
width_pattern += C16K_TABLE[values[current_row * 5 + i]]; |
infoSpace(values[current_row * 5 + i]); |
} |
width_pattern += C16K_START_STOP[C16K_STOP_VALUES[current_row]]; |
this.pattern[current_row] = width_pattern; |
this.row_height[current_row] = 10; |
} |
infoLine(); |
} |
private void getValueSubsetA(final int source, final int[] values, final int bar_chars) { |
if (source > 127) { |
if (source < 160) { |
values[bar_chars] = source + 64 - 128; |
} else { |
values[bar_chars] = source - 32 - 128; |
} |
} else { |
if (source < 32) { |
values[bar_chars] = source + 64; |
} else { |
values[bar_chars] = source - 32; |
} |
} |
} |
private void getValueSubsetB(final int source, final int[] values, final int bar_chars) { |
if (source > 127) { |
values[bar_chars] = source - 32 - 128; |
} else { |
values[bar_chars] = source - 32; |
} |
} |
private void getValueSubsetC(final int source_a, final int source_b, final int[] values, final int bar_chars) { |
int weight; |
weight = 10 * Character.getNumericValue(source_a) + Character.getNumericValue(source_b); |
values[bar_chars] = weight; |
} |
private Mode findSubset(final int letter) { |
Mode mode; |
if (letter <= 31) { |
mode = Mode.SHIFTA; |
} else if (letter >= 48 && letter <= 57) { |
mode = Mode.ABORC; |
} else if (letter <= 95) { |
mode = Mode.AORB; |
} else if (letter <= 127) { |
mode = Mode.SHIFTB; |
} else if (letter <= 159) { |
mode = Mode.SHIFTA; |
} else if (letter <= 223) { |
mode = Mode.AORB; |
} else { |
mode = Mode.SHIFTB; |
} |
return mode; |
} |
private void reduceSubsetChanges( |
final int block_count) { /* Implements rules from ISO 15417 Annex E */ |
int i, length; |
Mode current, last, next; |
for (i = 0; i < block_count; i++) { |
current = this.block_mode[i]; |
length = this.block_length[i]; |
if (i != 0) { |
last = this.block_mode[i - 1]; |
} else { |
last = Mode.NULL; |
} |
if (i != block_count - 1) { |
next = this.block_mode[i + 1]; |
} else { |
next = Mode.NULL; |
} |
if (i == 0) { /* first block */ |
if (block_count == 1 && length == 2 && current == Mode.ABORC) { /* Rule 1a */ |
this.block_mode[i] = Mode.LATCHC; |
} |
if (current == Mode.ABORC) { |
if (length >= 4) { /* Rule 1b */ |
this.block_mode[i] = Mode.LATCHC; |
} else { |
this.block_mode[i] = Mode.AORB; |
current = Mode.AORB; |
} |
} |
if (current == Mode.SHIFTA) { /* Rule 1c */ |
this.block_mode[i] = Mode.LATCHA; |
} |
if (current == Mode.AORB && next == Mode.SHIFTA) { /* Rule 1c */ |
this.block_mode[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.AORB) { /* Rule 1d */ |
this.block_mode[i] = Mode.LATCHB; |
} |
} else { |
if (current == Mode.ABORC && length >= 4) { /* Rule 3 */ |
this.block_mode[i] = Mode.LATCHC; |
current = Mode.LATCHC; |
} |
if (current == Mode.ABORC) { |
this.block_mode[i] = Mode.AORB; |
current = Mode.AORB; |
} |
if (current == Mode.AORB && last == Mode.LATCHA) { |
this.block_mode[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.AORB && last == Mode.LATCHB) { |
this.block_mode[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.AORB && next == Mode.SHIFTA) { |
this.block_mode[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.AORB && next == Mode.SHIFTB) { |
this.block_mode[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.AORB) { |
this.block_mode[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.SHIFTA && length > 1) { /* Rule 4 */ |
this.block_mode[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.SHIFTB && length > 1) { /* Rule 5 */ |
this.block_mode[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.SHIFTA && last == Mode.LATCHA) { |
this.block_mode[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.SHIFTB && last == Mode.LATCHB) { |
this.block_mode[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
if (current == Mode.SHIFTA && last == Mode.LATCHC) { |
this.block_mode[i] = Mode.LATCHA; |
current = Mode.LATCHA; |
} |
if (current == Mode.SHIFTB && last == Mode.LATCHC) { |
this.block_mode[i] = Mode.LATCHB; |
current = Mode.LATCHB; |
} |
} /* Rule 2 is implimented elsewhere, Rule 6 is implied */ |
} |
combineSubsetBlocks(block_count); |
} |
private void combineSubsetBlocks(int block_count) { |
int i, j; |
/* bring together same type blocks */ |
if (block_count > 1) { |
i = 1; |
while (i < block_count) { |
if (this.block_mode[i - 1] == this.block_mode[i]) { |
/* bring together */ |
this.block_length[i - 1] = this.block_length[i - 1] + this.block_length[i]; |
j = i + 1; |
/* decreace the list */ |
while (j < block_count) { |
this.block_length[j - 1] = this.block_length[j]; |
this.block_mode[j - 1] = this.block_mode[j]; |
j++; |
} |
block_count = block_count - 1; |
i--; |
} |
i++; |
} |
} |
} |
@Override |
protected void plotSymbol() { |
int xBlock, yBlock; |
int x, y, w, h; |
boolean black; |
this.rectangles.clear(); |
y = 1; |
h = 1; |
for (yBlock = 0; yBlock < this.row_count; yBlock++) { |
black = true; |
x = 15; |
for (xBlock = 0; xBlock < this.pattern[yBlock].length(); xBlock++) { |
if (black) { |
black = false; |
w = this.pattern[yBlock].charAt(xBlock) - '0'; |
if (this.row_height[yBlock] == -1) { |
h = this.default_height; |
} else { |
h = this.row_height[yBlock]; |
} |
if (w != 0 && h != 0) { |
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); |
this.rectangles.add(rect); |
} |
if (x + w > this.symbol_width) { |
this.symbol_width = x + w; |
} |
} else { |
black = true; |
} |
x += this.pattern[yBlock].charAt(xBlock) - '0'; |
} |
y += h; |
if (y > this.symbol_height) { |
this.symbol_height = y; |
} |
/* Add bars between rows */ |
if (yBlock != this.row_count - 1) { |
final Rectangle2D.Double rect = new Rectangle2D.Double(15, y - 1, this.symbol_width - 15, 2); |
this.rectangles.add(rect); |
} |
} |
/* Add top and bottom binding bars */ |
final Rectangle2D.Double top = new Rectangle2D.Double(0, 0, this.symbol_width + 15, 2); |
this.rectangles.add(top); |
final Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, this.symbol_width + 15, 2); |
this.rectangles.add(bottom); |
this.symbol_width += 15; |
this.symbol_height += 1; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Pharmazentralnummer.java |
---|
New file |
0,0 → 1,76 |
/* |
* Copyright 2015 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* PZN8 is a Code 39 based symbology used by the pharmaceutical industry in Germany. PZN8 encodes a |
* 7 digit number and includes a modulo-10 check digit. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Pharmazentralnummer extends Symbol { |
/* |
* Pharmazentral Nummer is a Code 3 of 9 symbol with an extra check digit. Now generates PZN-8. |
*/ |
@Override |
protected void encode() { |
final int l = this.content.length(); |
String localstr; |
int zeroes, count = 0, check_digit; |
final Code3Of9 c = new Code3Of9(); |
if (l > 7) { |
throw new OkapiException("Input data too long"); |
} |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
localstr = "-"; |
zeroes = 7 - l + 1; |
for (int i = 1; i < zeroes; i++) { |
localstr += '0'; |
} |
localstr += this.content; |
for (int i = 1; i < 8; i++) { |
count += i * Character.getNumericValue(localstr.charAt(i)); |
} |
check_digit = count % 11; |
if (check_digit == 11) { |
check_digit = 0; |
} |
if (check_digit == 10) { |
throw new OkapiException("Not a valid PZN identifier"); |
} |
infoLine("Check Digit: " + check_digit); |
localstr += (char) (check_digit + '0'); |
c.setContent(localstr); |
this.readable = "PZN" + localstr; |
this.pattern = new String[1]; |
this.pattern[0] = c.pattern[0]; |
this.row_count = 1; |
this.row_height = new int[1]; |
this.row_height[0] = -1; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/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/JapanPost.java |
---|
New file |
0,0 → 1,143 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.positionOf; |
import java.awt.geom.Rectangle2D; |
import java.util.Locale; |
/** |
* <p> |
* Implements the Japanese Postal Code symbology as used to encode address data for mail items in |
* Japan. Valid input characters are digits 0-9, characters A-Z and the dash (-) character. A |
* modulo-19 check digit is added and should not be included in the input data. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class JapanPost extends Symbol { |
private static final String[] JAPAN_TABLE = { "FFT", "FDA", "DFA", "FAD", "FTF", "DAF", "AFD", "ADF", "TFF", "FTT", "TFT", "DAT", "DTA", "ADT", "TDA", "ATD", "TAD", "TTF", "FFF" }; |
private static final char[] KASUT_SET = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; |
private static final char[] CH_KASUT_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; |
@Override |
protected void encode() { |
String dest; |
String inter; |
int i, sum, check; |
char c; |
this.content = this.content.toUpperCase(Locale.ENGLISH); |
if (!this.content.matches("[0-9A-Z\\-]+")) { |
throw new OkapiException("Invalid characters in data"); |
} |
inter = ""; |
for (i = 0; i < this.content.length() && inter.length() < 20; i++) { |
c = this.content.charAt(i); |
if (c >= '0' && c <= '9') { |
inter += c; |
} |
if (c == '-') { |
inter += c; |
} |
if (c >= 'A' && c <= 'J') { |
inter += 'a'; |
inter += CH_KASUT_SET[c - 'A']; |
} |
if (c >= 'K' && c <= 'O') { |
inter += 'b'; |
inter += CH_KASUT_SET[c - 'K']; |
} |
if (c >= 'U' && c <= 'Z') { |
inter += 'c'; |
inter += CH_KASUT_SET[c - 'U']; |
} |
} |
for (i = inter.length(); i < 20; i++) { |
inter += "d"; |
} |
dest = "FD"; |
sum = 0; |
for (i = 0; i < 20; i++) { |
dest += JAPAN_TABLE[positionOf(inter.charAt(i), KASUT_SET)]; |
sum += positionOf(inter.charAt(i), CH_KASUT_SET); |
} |
/* Calculate check digit */ |
check = 19 - sum % 19; |
if (check == 19) { |
check = 0; |
} |
dest += JAPAN_TABLE[positionOf(CH_KASUT_SET[check], KASUT_SET)]; |
dest += "DF"; |
infoLine("Encoding: " + dest); |
infoLine("Check Digit: " + check); |
this.readable = ""; |
this.pattern = new String[] { dest }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
@Override |
protected void plotSymbol() { |
int xBlock; |
int x, y, w, h; |
this.rectangles.clear(); |
x = 0; |
w = 1; |
y = 0; |
h = 0; |
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) { |
switch (this.pattern[0].charAt(xBlock)) { |
case 'A': |
y = 0; |
h = 5; |
break; |
case 'D': |
y = 3; |
h = 5; |
break; |
case 'F': |
y = 0; |
h = 8; |
break; |
case 'T': |
y = 3; |
h = 2; |
break; |
} |
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); |
this.rectangles.add(rect); |
x += 2; |
} |
this.symbol_width = this.pattern[0].length() * 3; |
this.symbol_height = 8; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/AztecCode.java |
---|
New file |
0,0 → 1,1824 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static java.nio.charset.StandardCharsets.US_ASCII; |
import static uk.org.okapibarcode.util.Arrays.insertArray; |
/** |
* <p> |
* Implements Aztec Code bar code symbology According to ISO/IEC 24778:2008. |
* |
* <p> |
* Aztec Code can encode 8-bit ISO 8859-1 (Latin-1) data (except 0x00 Null characters) up to a |
* maximum length of approximately 3800 numeric characters, 3000 alphabetic characters or 1900 bytes |
* of data in a two-dimensional matrix symbol. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class AztecCode extends Symbol { |
/* 27 x 27 data grid */ |
private static final int[] COMPACT_AZTEC_MAP = { 609, 608, 411, 413, 415, 417, 419, 421, 423, 425, 427, 429, 431, 433, 435, 437, 439, 441, 443, 445, 447, 449, 451, 453, 455, 457, 459, 607, 606, |
410, 412, 414, 416, 418, 420, 422, 424, 426, 428, 430, 432, 434, 436, 438, 440, 442, 444, 446, 448, 450, 452, 454, 456, 458, 605, 604, 409, 408, 243, 245, 247, 249, 251, 253, 255, 257, |
259, 261, 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 460, 461, 603, 602, 407, 406, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, |
278, 280, 282, 462, 463, 601, 600, 405, 404, 241, 240, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 284, 285, 464, 465, 599, 598, 403, 402, 239, |
238, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 286, 287, 466, 467, 597, 596, 401, 400, 237, 236, 105, 104, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, |
23, 25, 27, 140, 141, 288, 289, 468, 469, 595, 594, 399, 398, 235, 234, 103, 102, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 142, 143, 290, 291, 470, 471, 593, 592, 397, 396, 233, |
232, 101, 100, 1, 1, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 0, 1, 28, 29, 144, 145, 292, 293, 472, 473, 591, 590, 395, 394, 231, 230, 99, 98, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 30, 31, |
146, 147, 294, 295, 474, 475, 589, 588, 393, 392, 229, 228, 97, 96, 2027, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2007, 32, 33, 148, 149, 296, 297, 476, 477, 587, 586, 391, 390, 227, 226, 95, 94, 2026, |
1, 0, 1, 1, 1, 1, 1, 0, 1, 2008, 34, 35, 150, 151, 298, 299, 478, 479, 585, 584, 389, 388, 225, 224, 93, 92, 2025, 1, 0, 1, 0, 0, 0, 1, 0, 1, 2009, 36, 37, 152, 153, 300, 301, 480, 481, |
583, 582, 387, 386, 223, 222, 91, 90, 2024, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2010, 38, 39, 154, 155, 302, 303, 482, 483, 581, 580, 385, 384, 221, 220, 89, 88, 2023, 1, 0, 1, 0, 0, 0, 1, 0, 1, |
2011, 40, 41, 156, 157, 304, 305, 484, 485, 579, 578, 383, 382, 219, 218, 87, 86, 2022, 1, 0, 1, 1, 1, 1, 1, 0, 1, 2012, 42, 43, 158, 159, 306, 307, 486, 487, 577, 576, 381, 380, 217, 216, |
85, 84, 2021, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2013, 44, 45, 160, 161, 308, 309, 488, 489, 575, 574, 379, 378, 215, 214, 83, 82, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 47, 162, 163, 310, 311, 490, |
491, 573, 572, 377, 376, 213, 212, 81, 80, 0, 0, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 0, 0, 48, 49, 164, 165, 312, 313, 492, 493, 571, 570, 375, 374, 211, 210, 78, 76, 74, 72, 70, 68, |
66, 64, 62, 60, 58, 56, 54, 50, 51, 166, 167, 314, 315, 494, 495, 569, 568, 373, 372, 209, 208, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59, 57, 55, 52, 53, 168, 169, 316, 317, 496, 497, |
567, 566, 371, 370, 206, 204, 202, 200, 198, 196, 194, 192, 190, 188, 186, 184, 182, 180, 178, 176, 174, 170, 171, 318, 319, 498, 499, 565, 564, 369, 368, 207, 205, 203, 201, 199, 197, |
195, 193, 191, 189, 187, 185, 183, 181, 179, 177, 175, 172, 173, 320, 321, 500, 501, 563, 562, 366, 364, 362, 360, 358, 356, 354, 352, 350, 348, 346, 344, 342, 340, 338, 336, 334, 332, |
330, 328, 326, 322, 323, 502, 503, 561, 560, 367, 365, 363, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 337, 335, 333, 331, 329, 327, 324, 325, 504, 505, 558, 556, 554, |
552, 550, 548, 546, 544, 542, 540, 538, 536, 534, 532, 530, 528, 526, 524, 522, 520, 518, 516, 514, 512, 510, 506, 507, 559, 557, 555, 553, 551, 549, 547, 545, 543, 541, 539, 537, 535, |
533, 531, 529, 527, 525, 523, 521, 519, 517, 515, 513, 511, 508, 509 }; |
private static final int[][] AZTEC_MAP = new int[151][151]; |
/* |
* From Table 2: |
* |
* 1 = upper 2 = lower 4 = mixed 8 = punctuation 16 = digits 32 = binary |
* |
* Values can be OR'ed, so e.g. 12 = 4 | 8, and 23 = 1 | 2 | 4 | 16 |
*/ |
private static final int[] AZTEC_CODE_SET = { 32, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 4, 4, 4, 4, 4, 23, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, |
24, 8, 24, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 4, 8, 4, 4, 4, 2, 2, 2, 2, 2, 2, |
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 4, 8, 4, 4 }; |
/* From Table 2 */ |
private static final int[] AZTEC_SYMBOL_CHAR = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 300, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 19, 1, 6, 7, 8, 9, 10, 11, 12, |
13, 14, 15, 16, 301, 18, 302, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 21, 22, 23, 24, 25, 26, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, |
27, 27, 21, 28, 22, 23, 24, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 29, 25, 30, 26, 27 }; |
/* |
* Problem characters are: 300: Carriage Return (ASCII 13) 301: Comma (ASCII 44) 302: Full Stop |
* (ASCII 46) |
*/ |
private static final String[] PENTBIT = { "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000", |
"10001", "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010", "11011", "11100", "11101", "11110", "11111" }; |
private static final String[] QUADBIT = { "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" }; |
private static final String[] TRIBIT = { "000", "001", "010", "011", "100", "101", "110", "111" }; |
/* Codewords per symbol */ |
private static final int[] AZTEC_SIZES = { 21, 48, 60, 88, 120, 156, 196, 240, 230, 272, 316, 364, 416, 470, 528, 588, 652, 720, 790, 864, 940, 1020, 920, 992, 1066, 1144, 1224, 1306, 1392, 1480, |
1570, 1664 }; |
private static final int[] AZTEC_COMPACT_SIZES = { 17, 40, 51, 76 }; |
/* Data bits per symbol maximum with 10% error correction */ |
private static final int[] AZTEC_10_DATA_SIZES = { 96, 246, 408, 616, 840, 1104, 1392, 1704, 2040, 2420, 2820, 3250, 3720, 4200, 4730, 5270, 5840, 6450, 7080, 7750, 8430, 9150, 9900, 10680, 11484, |
12324, 13188, 14076, 15000, 15948, 16920, 17940 }; |
/* Data bits per symbol maximum with 23% error correction */ |
private static final int[] AZTEC_23_DATA_SIZES = { 84, 204, 352, 520, 720, 944, 1184, 1456, 1750, 2070, 2410, 2780, 3180, 3590, 4040, 4500, 5000, 5520, 6060, 6630, 7210, 7830, 8472, 9132, 9816, |
10536, 11280, 12036, 12828, 13644, 14472, 15348 }; |
/* Data bits per symbol maximum with 36% error correction */ |
private static final int[] AZTEC_36_DATA_SIZES = { 66, 168, 288, 432, 592, 776, 984, 1208, 1450, 1720, 2000, 2300, 2640, 2980, 3350, 3740, 4150, 4580, 5030, 5500, 5990, 6500, 7032, 7584, 8160, |
8760, 9372, 9996, 10656, 11340, 12024, 12744 }; |
/* Data bits per symbol maximum with 50% error correction */ |
private static final int[] AZTEC_50_DATA_SIZES = { 48, 126, 216, 328, 456, 600, 760, 936, 1120, 1330, 1550, 1790, 2050, 2320, 2610, 2910, 3230, 3570, 3920, 4290, 4670, 5070, 5484, 5916, 6360, |
6828, 7308, 7800, 8316, 8844, 9384, 9948 }; |
private static final int[] AZTEC_COMPACT_10_DATA_SIZES = { 78, 198, 336, 520 }; |
private static final int[] AZTEC_COMPACT_23_DATA_SIZES = { 66, 168, 288, 440 }; |
private static final int[] AZTEC_COMPACT_36_DATA_SIZES = { 48, 138, 232, 360 }; |
private static final int[] AZTEC_COMPACT_50_DATA_SIZES = { 36, 102, 176, 280 }; |
private static final int[] AZTEC_OFFSET = { 66, 64, 62, 60, 57, 55, 53, 51, 49, 47, 45, 42, 40, 38, 36, 34, 32, 30, 28, 25, 23, 21, 19, 17, 15, 13, 10, 8, 6, 4, 2, 0 }; |
private static final int[] AZTEC_COMPACT_OFFSET = { 6, 4, 2, 0 }; |
/* Initialize AZTEC_MAP */ |
static { |
int layer, start, length, n, i; |
int x, y; |
for (x = 0; x < 151; x++) { |
for (y = 0; y < 151; y++) { |
AZTEC_MAP[x][y] = 0; |
} |
} |
for (layer = 1; layer < 33; layer++) { |
start = 112 * (layer - 1) + 16 * (layer - 1) * (layer - 1) + 2; |
length = 28 + (layer - 1) * 4 + layer * 4; |
/* Top */ |
i = 0; |
x = 64 - (layer - 1) * 2; |
y = 63 - (layer - 1) * 2; |
for (n = start; n < start + length; n += 2) { |
AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y)] = n; |
AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y - 1)] = n + 1; |
i++; |
} |
/* Right */ |
i = 0; |
x = 78 + (layer - 1) * 2; |
y = 64 - (layer - 1) * 2; |
for (n = start + length; n < start + length * 2; n += 2) { |
AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y + i)] = n; |
AZTEC_MAP[avoidReferenceGrid(x + 1)][avoidReferenceGrid(y + i)] = n + 1; |
i++; |
} |
/* Bottom */ |
i = 0; |
x = 77 + (layer - 1) * 2; |
y = 78 + (layer - 1) * 2; |
for (n = start + length * 2; n < start + length * 3; n += 2) { |
AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y)] = n; |
AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y + 1)] = n + 1; |
i++; |
} |
/* Left */ |
i = 0; |
x = 63 - (layer - 1) * 2; |
y = 77 + (layer - 1) * 2; |
for (n = start + length * 3; n < start + length * 4; n += 2) { |
AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y - i)] = n; |
AZTEC_MAP[avoidReferenceGrid(x - 1)][avoidReferenceGrid(y - i)] = n + 1; |
i++; |
} |
} |
/* Central finder pattern */ |
for (y = 69; y <= 81; y++) { |
for (x = 69; x <= 81; x++) { |
AZTEC_MAP[x][y] = 1; |
} |
} |
for (y = 70; y <= 80; y++) { |
for (x = 70; x <= 80; x++) { |
AZTEC_MAP[x][y] = 0; |
} |
} |
for (y = 71; y <= 79; y++) { |
for (x = 71; x <= 79; x++) { |
AZTEC_MAP[x][y] = 1; |
} |
} |
for (y = 72; y <= 78; y++) { |
for (x = 72; x <= 78; x++) { |
AZTEC_MAP[x][y] = 0; |
} |
} |
for (y = 73; y <= 77; y++) { |
for (x = 73; x <= 77; x++) { |
AZTEC_MAP[x][y] = 1; |
} |
} |
for (y = 74; y <= 76; y++) { |
for (x = 74; x <= 76; x++) { |
AZTEC_MAP[x][y] = 0; |
} |
} |
/* Guide bars */ |
for (y = 11; y < 151; y += 16) { |
for (x = 1; x < 151; x += 2) { |
AZTEC_MAP[x][y] = 1; |
AZTEC_MAP[y][x] = 1; |
} |
} |
/* Descriptor */ |
for (i = 0; i < 10; i++) { /* Top */ |
AZTEC_MAP[avoidReferenceGrid(66 + i)][avoidReferenceGrid(64)] = 20000 + i; |
} |
for (i = 0; i < 10; i++) { /* Right */ |
AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(66 + i)] = 20010 + i; |
} |
for (i = 0; i < 10; i++) { /* Bottom */ |
AZTEC_MAP[avoidReferenceGrid(75 - i)][avoidReferenceGrid(77)] = 20020 + i; |
} |
for (i = 0; i < 10; i++) { /* Left */ |
AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(75 - i)] = 20030 + i; |
} |
/* Orientation */ |
AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(64)] = 1; |
AZTEC_MAP[avoidReferenceGrid(65)][avoidReferenceGrid(64)] = 1; |
AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(65)] = 1; |
AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(64)] = 1; |
AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(65)] = 1; |
AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(76)] = 1; |
} |
private static int avoidReferenceGrid(final int input) { |
int output = input; |
if (output > 10) { |
output++; |
} |
if (output > 26) { |
output++; |
} |
if (output > 42) { |
output++; |
} |
if (output > 58) { |
output++; |
} |
if (output > 74) { |
output++; |
} |
if (output > 90) { |
output++; |
} |
if (output > 106) { |
output++; |
} |
if (output > 122) { |
output++; |
} |
if (output > 138) { |
output++; |
} |
return output; |
} |
private int preferredSize = 0; |
private int preferredEccLevel = 2; |
private String structuredAppendMessageId; |
private int structuredAppendPosition = 1; |
private int structuredAppendTotal = 1; |
/** |
* <p> |
* Sets a preferred symbol size. This value may be ignored if data string is too large to fit in |
* the specified symbol size. Values correspond to symbol sizes as shown in the following table: |
* |
* <table summary="Available Aztec Code symbol sizes"> |
* <tbody> |
* <tr> |
* <th>Input</th> |
* <th>Symbol Size</th> |
* <th>Input</th> |
* <th>Symbol Size</th> |
* </tr> |
* <tr> |
* <td>1</td> |
* <td>15 x 15</td> |
* <td>19</td> |
* <td>79 x 79</td> |
* </tr> |
* <tr> |
* <td>2</td> |
* <td>19 x 19</td> |
* <td>20</td> |
* <td>83 x 83</td> |
* </tr> |
* <tr> |
* <td>3</td> |
* <td>23 x 23</td> |
* <td>21</td> |
* <td>87 x 87</td> |
* </tr> |
* <tr> |
* <td>4</td> |
* <td>27 x 27</td> |
* <td>22</td> |
* <td>91 x 91</td> |
* </tr> |
* <tr> |
* <td>5</td> |
* <td>19 x 19</td> |
* <td>23</td> |
* <td>95 x 95</td> |
* </tr> |
* <tr> |
* <td>6</td> |
* <td>23 x 23</td> |
* <td>24</td> |
* <td>101 x 101</td> |
* </tr> |
* <tr> |
* <td>7</td> |
* <td>27 x 27</td> |
* <td>25</td> |
* <td>105 x 105</td> |
* </tr> |
* <tr> |
* <td>8</td> |
* <td>31 x 31</td> |
* <td>26</td> |
* <td>109 x 109</td> |
* </tr> |
* <tr> |
* <td>9</td> |
* <td>37 x 37</td> |
* <td>27</td> |
* <td>113 x 113</td> |
* </tr> |
* <tr> |
* <td>10</td> |
* <td>41 x 41</td> |
* <td>28</td> |
* <td>117 x 117</td> |
* </tr> |
* <tr> |
* <td>11</td> |
* <td>45 x 45</td> |
* <td>29</td> |
* <td>121 x 121</td> |
* </tr> |
* <tr> |
* <td>12</td> |
* <td>49 x 49</td> |
* <td>30</td> |
* <td>125 x 125</td> |
* </tr> |
* <tr> |
* <td>13</td> |
* <td>53 x 53</td> |
* <td>31</td> |
* <td>131 x 131</td> |
* </tr> |
* <tr> |
* <td>14</td> |
* <td>57 x 57</td> |
* <td>32</td> |
* <td>135 x 135</td> |
* </tr> |
* <tr> |
* <td>15</td> |
* <td>61 x 61</td> |
* <td>33</td> |
* <td>139 x 139</td> |
* </tr> |
* <tr> |
* <td>16</td> |
* <td>67 x 67</td> |
* <td>34</td> |
* <td>143 x 143</td> |
* </tr> |
* <tr> |
* <td>17</td> |
* <td>71 x 71</td> |
* <td>35</td> |
* <td>147 x 147</td> |
* </tr> |
* <tr> |
* <td>18</td> |
* <td>75 x 75</td> |
* <td>36</td> |
* <td>151 x 151</td> |
* </tr> |
* </tbody> |
* </table> |
* |
* <p> |
* Note that sizes 1 to 4 are the "compact" Aztec Code symbols; sizes 5 to 36 are the |
* "full-range" Aztec Code symbols. |
* |
* @param size an integer in the range 1 - 36 |
*/ |
public void setPreferredSize(final int size) { |
if (size < 1 || size > 36) { |
throw new IllegalArgumentException("Invalid size: " + size); |
} |
this.preferredSize = size; |
} |
/** |
* Returns the preferred symbol size. |
* |
* @return the preferred symbol size |
*/ |
public int getPreferredSize() { |
return this.preferredSize; |
} |
/** |
* Sets the preferred minimum amount of symbol space dedicated to error correction. This value |
* will be ignored if a symbol size has been set by <code>setPreferredSize</code>. Valid options |
* are: |
* |
* <table summary="Error correction options"> |
* <tbody> |
* <tr> |
* <th>Mode</th> |
* <th>Error Correction Capacity</th> |
* </tr> |
* <tr> |
* <td>1</td> |
* <td>> 10% + 3 codewords</td> |
* </tr> |
* <tr> |
* <td>2</td> |
* <td>> 23% + 3 codewords</td> |
* </tr> |
* <tr> |
* <td>3</td> |
* <td>> 36% + 3 codewords</td> |
* </tr> |
* <tr> |
* <td>4</td> |
* <td>> 50% + 3 codewords</td> |
* </tr> |
* </tbody> |
* </table> |
* |
* @param eccLevel an integer in the range 1 - 4 |
*/ |
public void setPreferredEccLevel(final int eccLevel) { |
if (eccLevel < 1 || eccLevel > 4) { |
throw new IllegalArgumentException("Invalid ECC level: " + eccLevel); |
} |
this.preferredEccLevel = eccLevel; |
} |
/** |
* Returns the preferred error correction level. |
* |
* @return the preferred error correction level |
*/ |
public int getPreferredEccLevel() { |
return this.preferredEccLevel; |
} |
/** |
* If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured |
* format, this method sets the position of this symbol in the series. Valid values are 1 |
* through 26 inclusive. |
* |
* @param position the position of this Aztec Code symbol in the structured append series |
*/ |
public void setStructuredAppendPosition(final int position) { |
if (position < 1 || position > 26) { |
throw new IllegalArgumentException("Invalid Aztec Code structured append position: " + position); |
} |
this.structuredAppendPosition = position; |
} |
/** |
* Returns the position of this Aztec Code symbol in a series of symbols using structured |
* append. If this symbol is not part of such a series, this method will return <code>1</code>. |
* |
* @return the position of this Aztec Code symbol in a series of symbols using structured append |
*/ |
public int getStructuredAppendPosition() { |
return this.structuredAppendPosition; |
} |
/** |
* If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured |
* format, this method sets the total number of symbols in the series. Valid values are 1 |
* through 26 inclusive. A value of 1 indicates that this symbol is not part of a structured |
* append series. |
* |
* @param total the total number of Aztec Code symbols in the structured append series |
*/ |
public void setStructuredAppendTotal(final int total) { |
if (total < 1 || total > 26) { |
throw new IllegalArgumentException("Invalid Aztec Code structured append total: " + total); |
} |
this.structuredAppendTotal = total; |
} |
/** |
* Returns the size of the series of Aztec Code symbols using structured append that this symbol |
* is part of. If this symbol is not part of a structured append series, this method will return |
* <code>1</code>. |
* |
* @return size of the series that this symbol is part of |
*/ |
public int getStructuredAppendTotal() { |
return this.structuredAppendTotal; |
} |
/** |
* If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured |
* format, this method sets the unique message ID for the series. Values may not contain spaces |
* and must contain only printable ASCII characters. Message IDs are optional. |
* |
* @param messageId the unique message ID for the series that this symbol is part of |
*/ |
public void setStructuredAppendMessageId(final String messageId) { |
if (messageId != null && !messageId.matches("^[\\x21-\\x7F]+$")) { |
throw new IllegalArgumentException("Invalid Aztec Code structured append message ID: " + messageId); |
} |
this.structuredAppendMessageId = messageId; |
} |
/** |
* Returns the unique message ID of the series of Aztec Code symbols using structured append |
* that this symbol is part of. If this symbol is not part of a structured append series, this |
* method will return <code>null</code>. |
* |
* @return the unique message ID for the series that this symbol is part of |
*/ |
public String getStructuredAppendMessageId() { |
return this.structuredAppendMessageId; |
} |
@Override |
protected boolean gs1Supported() { |
return true; |
} |
@Override |
protected void encode() { |
int layers; |
boolean compact; |
StringBuilder adjustedString; |
if (this.inputDataType == DataType.GS1 && this.readerInit) { |
throw new OkapiException("Cannot encode in GS1 and Reader Initialisation mode at the same time"); |
} |
eciProcess(); // Get ECI mode |
/* Optional structured append (Section 8 of spec) */ |
/* ML + UL start flag handled later, not part of data */ |
if (this.structuredAppendTotal != 1) { |
final StringBuilder prefix = new StringBuilder(); |
if (this.structuredAppendMessageId != null) { |
prefix.append(' ').append(this.structuredAppendMessageId).append(' '); |
} |
prefix.append((char) (this.structuredAppendPosition + 64)); // 1-26 as A-Z |
prefix.append((char) (this.structuredAppendTotal + 64)); // 1-26 as A-Z |
final int[] prefixArray = toBytes(prefix.toString(), US_ASCII); |
this.inputData = insertArray(this.inputData, 0, prefixArray); |
} |
final String binaryString = generateAztecBinary(); |
int dataLength = binaryString.length(); |
if (this.preferredSize == 0) { |
/* The size of the symbol can be determined by Okapi */ |
int dataMaxSize = 0; |
final int compLoop = this.readerInit ? 1 : 4; |
do { |
/* Decide what size symbol to use - the smallest that fits the data */ |
int[] dataSizes; |
int[] compactDataSizes; |
switch (this.preferredEccLevel) { |
/* |
* For each level of error correction work out the smallest symbol which the data |
* will fit in |
*/ |
case 1: |
dataSizes = AZTEC_10_DATA_SIZES; |
compactDataSizes = AZTEC_COMPACT_10_DATA_SIZES; |
break; |
case 2: |
dataSizes = AZTEC_23_DATA_SIZES; |
compactDataSizes = AZTEC_COMPACT_23_DATA_SIZES; |
break; |
case 3: |
dataSizes = AZTEC_36_DATA_SIZES; |
compactDataSizes = AZTEC_COMPACT_36_DATA_SIZES; |
break; |
case 4: |
dataSizes = AZTEC_50_DATA_SIZES; |
compactDataSizes = AZTEC_COMPACT_50_DATA_SIZES; |
break; |
default: |
throw new OkapiException("Unrecognized ECC level: " + this.preferredEccLevel); |
} |
layers = 0; |
compact = false; |
for (int i = 32; i > 0; i--) { |
if (dataLength < dataSizes[i - 1]) { |
layers = i; |
compact = false; |
dataMaxSize = dataSizes[i - 1]; |
} |
} |
for (int i = compLoop; i > 0; i--) { |
if (dataLength < compactDataSizes[i - 1]) { |
layers = i; |
compact = true; |
dataMaxSize = compactDataSizes[i - 1]; |
} |
} |
if (layers == 0) { |
/* Couldn't find a symbol which fits the data */ |
throw new OkapiException("Input too long (too many bits for selected ECC)"); |
} |
adjustedString = adjustBinaryString(binaryString, compact, layers); |
dataLength = adjustedString.length(); |
} while (dataLength > dataMaxSize); |
/* |
* This loop will only repeat on the rare occasions when the rule about not having all |
* 1s or all 0s means that the binary string has had to be lengthened beyond the maximum |
* number of bits that can be encoded in a symbol of the selected size |
*/ |
} else { |
/* The size of the symbol has been specified by the user */ |
if (this.preferredSize >= 1 && this.preferredSize <= 4) { |
compact = true; |
layers = this.preferredSize; |
} else { |
compact = false; |
layers = this.preferredSize - 4; |
} |
adjustedString = adjustBinaryString(binaryString, compact, layers); |
/* Check if the data actually fits into the selected symbol size */ |
final int codewordSize = getCodewordSize(layers); |
final int[] sizes = compact ? AZTEC_COMPACT_SIZES : AZTEC_SIZES; |
final int dataMaxSize = codewordSize * (sizes[layers - 1] - 3); |
if (adjustedString.length() > dataMaxSize) { |
throw new OkapiException("Data too long for specified Aztec Code symbol size"); |
} |
} |
if (this.readerInit && compact && layers > 1) { |
throw new OkapiException("Symbol is too large for reader initialization"); |
} |
if (this.readerInit && layers > 22) { |
throw new OkapiException("Symbol is too large for reader initialization"); |
} |
final int codewordSize = getCodewordSize(layers); |
final int dataBlocks = adjustedString.length() / codewordSize; |
int eccBlocks; |
if (compact) { |
eccBlocks = AZTEC_COMPACT_SIZES[layers - 1] - dataBlocks; |
} else { |
eccBlocks = AZTEC_SIZES[layers - 1] - dataBlocks; |
} |
infoLine("Compact Mode: " + compact); |
infoLine("Layers: " + layers); |
infoLine("Codeword Length: " + codewordSize + " bits"); |
infoLine("Data Codewords: " + dataBlocks); |
infoLine("ECC Codewords: " + eccBlocks); |
/* Add ECC data to the adjusted string */ |
addErrorCorrection(adjustedString, codewordSize, dataBlocks, eccBlocks); |
/* Invert the data so that actual data is on the outside and reed-solomon on the inside */ |
for (int i = 0; i < adjustedString.length() / 2; i++) { |
final int mirror = adjustedString.length() - i - 1; |
final char c = adjustedString.charAt(i); |
adjustedString.setCharAt(i, adjustedString.charAt(mirror)); |
adjustedString.setCharAt(mirror, c); |
} |
/* Create the descriptor / mode message */ |
final String descriptor = createDescriptor(compact, layers, dataBlocks); |
/* Plot all of the data into the symbol in pre-defined spiral pattern */ |
if (compact) { |
this.readable = ""; |
this.row_count = 27 - 2 * AZTEC_COMPACT_OFFSET[layers - 1]; |
this.row_height = new int[this.row_count]; |
this.row_height[0] = -1; |
this.pattern = new String[this.row_count]; |
for (int y = AZTEC_COMPACT_OFFSET[layers - 1]; y < 27 - AZTEC_COMPACT_OFFSET[layers - 1]; y++) { |
final StringBuilder bin = new StringBuilder(27); |
for (int x = AZTEC_COMPACT_OFFSET[layers - 1]; x < 27 - AZTEC_COMPACT_OFFSET[layers - 1]; x++) { |
final int j = COMPACT_AZTEC_MAP[y * 27 + x]; |
if (j == 0) { |
bin.append('0'); |
} |
if (j == 1) { |
bin.append('1'); |
} |
if (j >= 2) { |
if (j - 2 < adjustedString.length()) { |
bin.append(adjustedString.charAt(j - 2)); |
} else { |
if (j >= 2000) { |
bin.append(descriptor.charAt(j - 2000)); |
} else { |
bin.append('0'); |
} |
} |
} |
} |
this.row_height[y - AZTEC_COMPACT_OFFSET[layers - 1]] = 1; |
this.pattern[y - AZTEC_COMPACT_OFFSET[layers - 1]] = bin2pat(bin); |
} |
} else { |
this.readable = ""; |
this.row_count = 151 - 2 * AZTEC_OFFSET[layers - 1]; |
this.row_height = new int[this.row_count]; |
this.row_height[0] = -1; |
this.pattern = new String[this.row_count]; |
for (int y = AZTEC_OFFSET[layers - 1]; y < 151 - AZTEC_OFFSET[layers - 1]; y++) { |
final StringBuilder bin = new StringBuilder(151); |
for (int x = AZTEC_OFFSET[layers - 1]; x < 151 - AZTEC_OFFSET[layers - 1]; x++) { |
final int j = AZTEC_MAP[x][y]; |
if (j == 1) { |
bin.append('1'); |
} |
if (j == 0) { |
bin.append('0'); |
} |
if (j >= 2) { |
if (j - 2 < adjustedString.length()) { |
bin.append(adjustedString.charAt(j - 2)); |
} else { |
if (j >= 20000) { |
bin.append(descriptor.charAt(j - 20000)); |
} else { |
bin.append('0'); |
} |
} |
} |
} |
this.row_height[y - AZTEC_OFFSET[layers - 1]] = 1; |
this.pattern[y - AZTEC_OFFSET[layers - 1]] = bin2pat(bin); |
} |
} |
} |
private String generateAztecBinary() { |
/* Encode input data into a binary string */ |
int i, j, k, bytes; |
int curtable, newtable, lasttable, chartype, maplength, blocks; |
final int[] charmap = new int[2 * this.inputData.length]; |
final int[] typemap = new int[2 * this.inputData.length]; |
final int[] blockType = new int[this.inputData.length + 1]; |
final int[] blockLength = new int[this.inputData.length + 1]; |
/* Lookup input string in encoding table */ |
maplength = 0; |
/* Add FNC1 to beginning of GS1 messages */ |
if (this.inputDataType == DataType.GS1) { |
charmap[maplength] = 0; // FLG |
typemap[maplength++] = 8; // PUNC |
charmap[maplength] = 400; // (0) |
typemap[maplength++] = 8; // PUNC |
} |
if (this.eciMode != 3) { |
int flagNumber; |
charmap[maplength] = 0; // FLG |
typemap[maplength++] = 8; // PUNC |
flagNumber = 6; |
if (this.eciMode < 100000) { |
flagNumber = 5; |
} |
if (this.eciMode < 10000) { |
flagNumber = 4; |
} |
if (this.eciMode < 1000) { |
flagNumber = 3; |
} |
if (this.eciMode < 100) { |
flagNumber = 2; |
} |
if (this.eciMode < 10) { |
flagNumber = 1; |
} |
charmap[maplength] = 400 + flagNumber; |
typemap[maplength++] = 8; // PUNC |
} |
for (i = 0; i < this.inputData.length; i++) { |
if (this.inputData[i] == FNC1) { |
/* FNC1 represented by FLG(0) */ |
charmap[maplength] = 0; // FLG |
typemap[maplength++] = 8; // PUNC |
charmap[maplength] = 400; // (0) |
typemap[maplength++] = 8; // PUNC |
} else { |
if (this.inputData[i] > 0x7F || this.inputData[i] == 0x00) { |
charmap[maplength] = this.inputData[i]; |
typemap[maplength++] = 32; // BINARY |
} else { |
charmap[maplength] = AZTEC_SYMBOL_CHAR[this.inputData[i]]; |
typemap[maplength++] = AZTEC_CODE_SET[this.inputData[i]]; |
} |
} |
} |
/* Look for double character encoding possibilities */ |
for (i = 0; i < maplength - 1; i++) { |
if (charmap[i] == 300 && charmap[i + 1] == 11 && typemap[i] == 12 && typemap[i + 1] == 4) { |
/* CR LF combination */ |
charmap[i] = 2; |
typemap[i] = 8; // PUNC |
if (i + 1 != maplength) { |
for (j = i + 1; j < maplength; j++) { |
charmap[j] = charmap[j + 1]; |
typemap[j] = typemap[j + 1]; |
} |
} |
maplength--; |
} |
if (charmap[i] == 302 && charmap[i + 1] == 1 && typemap[i] == 24 && typemap[i + 1] == 23) { |
/* . SP combination */ |
charmap[i] = 3; |
typemap[i] = 8; // PUNC; |
if (i + 1 != maplength) { |
for (j = i + 1; j < maplength; j++) { |
charmap[j] = charmap[j + 1]; |
typemap[j] = typemap[j + 1]; |
} |
} |
maplength--; |
} |
if (charmap[i] == 301 && charmap[i + 1] == 1 && typemap[i] == 24 && typemap[i + 1] == 23) { |
/* , SP combination */ |
charmap[i] = 4; |
typemap[i] = 8; // PUNC; |
if (i + 1 != maplength) { |
for (j = i + 1; j < maplength; j++) { |
charmap[j] = charmap[j + 1]; |
typemap[j] = typemap[j + 1]; |
} |
} |
maplength--; |
} |
if (charmap[i] == 21 && charmap[i + 1] == 1 && typemap[i] == 8 && typemap[i + 1] == 23) { |
/* : SP combination */ |
charmap[i] = 5; |
typemap[i] = 8; // PUNC; |
if (i + 1 != maplength) { |
for (j = i + 1; j < maplength; j++) { |
charmap[j] = charmap[j + 1]; |
typemap[j] = typemap[j + 1]; |
} |
} |
maplength--; |
} |
} |
/* look for blocks of characters which use the same table */ |
blocks = 0; |
for (i = 0; i < maplength; i++) { |
if (i > 0 && typemap[i] == typemap[i - 1]) { |
blockLength[blocks - 1]++; |
} else { |
blocks++; |
blockType[blocks - 1] = typemap[i]; |
blockLength[blocks - 1] = 1; |
} |
} |
if ((blockType[0] & 1) != 0) { |
blockType[0] = 1; |
} |
if ((blockType[0] & 2) != 0) { |
blockType[0] = 2; |
} |
if ((blockType[0] & 4) != 0) { |
blockType[0] = 4; |
} |
if ((blockType[0] & 8) != 0) { |
blockType[0] = 8; |
} |
if (blocks > 1) { |
/* look for adjacent blocks which can use the same table (left to right search) */ |
for (i = 1; i < blocks; i++) { |
if ((blockType[i] & blockType[i - 1]) != 0) { |
blockType[i] = blockType[i] & blockType[i - 1]; |
} |
} |
if ((blockType[blocks - 1] & 1) != 0) { |
blockType[blocks - 1] = 1; |
} |
if ((blockType[blocks - 1] & 2) != 0) { |
blockType[blocks - 1] = 2; |
} |
if ((blockType[blocks - 1] & 4) != 0) { |
blockType[blocks - 1] = 4; |
} |
if ((blockType[blocks - 1] & 8) != 0) { |
blockType[blocks - 1] = 8; |
} |
/* look for adjacent blocks which can use the same table (right to left search) */ |
for (i = blocks - 2; i > 0; i--) { |
if ((blockType[i] & blockType[i + 1]) != 0) { |
blockType[i] = blockType[i] & blockType[i + 1]; |
} |
} |
/* determine the encoding table for characters which do not fit with adjacent blocks */ |
for (i = 1; i < blocks; i++) { |
if ((blockType[i] & 8) != 0) { |
blockType[i] = 8; |
} |
if ((blockType[i] & 4) != 0) { |
blockType[i] = 4; |
} |
if ((blockType[i] & 2) != 0) { |
blockType[i] = 2; |
} |
if ((blockType[i] & 1) != 0) { |
blockType[i] = 1; |
} |
} |
/* |
* if less than 4 characters are preceded and followed by binary blocks then it is more |
* efficient to also encode these in binary |
*/ |
// for (i = 1; i < blocks - 1; i++) { |
// if ((blockType[i - 1] == 32) && (blockLength[i] < 4)) { |
// int nonBinaryLength = blockLength[i]; |
// for (int l = i; ((l < blocks) && (blockType[l] != 32)); l++) { |
// nonBinaryLength += blockLength[l]; |
// } |
// if (nonBinaryLength < 4) { |
// blockType[i] = 32; |
// } |
// } |
// } |
/* Combine blocks of the same type */ |
i = 0; |
do { |
if (blockType[i] == blockType[i + 1]) { |
blockLength[i] += blockLength[i + 1]; |
for (j = i + 1; j < blocks - 1; j++) { |
blockType[j] = blockType[j + 1]; |
blockLength[j] = blockLength[j + 1]; |
} |
blocks--; |
} else { |
i++; |
} |
} while (i < blocks - 1); |
} |
/* Put the adjusted block data back into typemap */ |
j = 0; |
for (i = 0; i < blocks; i++) { |
if (blockLength[i] < 3 && blockType[i] != 32) { /* Shift character(s) needed */ |
for (k = 0; k < blockLength[i]; k++) { |
typemap[j + k] = blockType[i] + 64; |
} |
} else { /* Latch character (or byte mode) needed */ |
for (k = 0; k < blockLength[i]; k++) { |
typemap[j + k] = blockType[i]; |
} |
} |
j += blockLength[i]; |
} |
/* Don't shift an initial capital letter */ |
if (maplength > 0 && typemap[0] == 65) { |
typemap[0] = 1; |
} |
/* |
* Problem characters (those that appear in different tables with different values) can now |
* be resolved into their tables |
*/ |
for (i = 0; i < maplength; i++) { |
if (charmap[i] >= 300 && charmap[i] < 400) { |
curtable = typemap[i]; |
if (curtable > 64) { |
curtable -= 64; |
} |
switch (charmap[i]) { |
case 300: |
/* Carriage Return */ |
switch (curtable) { |
case 8: |
charmap[i] = 1; |
break; // PUNC |
case 4: |
charmap[i] = 14; |
break; // PUNC |
} |
break; |
case 301: |
/* Comma */ |
switch (curtable) { |
case 8: |
charmap[i] = 17; |
break; // PUNC |
case 16: |
charmap[i] = 12; |
break; // DIGIT |
} |
break; |
case 302: |
/* Full Stop */ |
switch (curtable) { |
case 8: |
charmap[i] = 19; |
break; // PUNC |
case 16: |
charmap[i] = 13; |
break; // DIGIT |
} |
break; |
} |
} |
} |
final StringBuilder binaryString = new StringBuilder(); |
info("Encoding: "); |
curtable = 1; /* start with 1 table */ |
lasttable = 1; |
/* Optional structured append start flag (Section 8 of spec) */ |
if (this.structuredAppendTotal != 1) { |
binaryString.append(PENTBIT[29]); |
info("ML "); |
binaryString.append(PENTBIT[29]); |
info("UL "); |
} |
for (i = 0; i < maplength; i++) { |
newtable = curtable; |
if (typemap[i] != curtable && charmap[i] < 400) { |
/* Change table */ |
if (curtable == 32) { |
/* |
* If ending binary mode the current table is the same as when entering binary |
* mode |
*/ |
curtable = lasttable; |
newtable = lasttable; |
} |
if (typemap[i] > 64) { |
/* Shift character */ |
switch (typemap[i]) { |
case 64 + 1: |
/* To UPPER */ |
switch (curtable) { |
case 2: |
/* US */ |
binaryString.append(PENTBIT[28]); |
info("US "); |
break; |
case 4: |
/* UL */ |
binaryString.append(PENTBIT[29]); |
info("UL "); |
newtable = 1; |
break; |
case 8: |
/* UL */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
newtable = 1; |
break; |
case 16: |
/* US */ |
binaryString.append(QUADBIT[15]); |
info("US "); |
break; |
} |
break; |
case 64 + 2: |
/* To LOWER */ |
switch (curtable) { |
case 1: |
/* LL */ |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
case 4: |
/* LL */ |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
case 8: |
/* UL LL */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
case 16: |
/* UL LL */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
} |
break; |
case 64 + 4: |
/* To MIXED */ |
switch (curtable) { |
case 1: |
/* ML */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
case 2: |
/* ML */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
case 8: |
/* UL ML */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
case 16: |
/* UL ML */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
} |
break; |
case 64 + 8: |
/* To PUNC */ |
switch (curtable) { |
case 1: |
/* PS */ |
binaryString.append(PENTBIT[0]); |
info("PS "); |
break; |
case 2: |
/* PS */ |
binaryString.append(PENTBIT[0]); |
info("PS "); |
break; |
case 4: |
/* PS */ |
binaryString.append(PENTBIT[0]); |
info("PS "); |
break; |
case 16: |
/* PS */ |
binaryString.append(QUADBIT[0]); |
info("PS "); |
break; |
} |
break; |
case 64 + 16: |
/* To DIGIT */ |
switch (curtable) { |
case 1: |
/* DL */ |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
case 2: |
/* DL */ |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
case 4: |
/* UL DL */ |
binaryString.append(PENTBIT[29]); |
info("UL "); |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
case 8: |
/* UL DL */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
} |
break; |
} |
} else { |
/* Latch character */ |
switch (typemap[i]) { |
case 1: |
/* To UPPER */ |
switch (curtable) { |
case 2: |
/* ML UL */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
binaryString.append(PENTBIT[29]); |
info("UL "); |
newtable = 1; |
break; |
case 4: |
/* UL */ |
binaryString.append(PENTBIT[29]); |
info("UL "); |
newtable = 1; |
break; |
case 8: |
/* UL */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
newtable = 1; |
break; |
case 16: |
/* UL */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
newtable = 1; |
break; |
} |
break; |
case 2: |
/* To LOWER */ |
switch (curtable) { |
case 1: |
/* LL */ |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
case 4: |
/* LL */ |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
case 8: |
/* UL LL */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
case 16: |
/* UL LL */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
binaryString.append(PENTBIT[28]); |
info("LL "); |
newtable = 2; |
break; |
} |
break; |
case 4: |
/* To MIXED */ |
switch (curtable) { |
case 1: |
/* ML */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
case 2: |
/* ML */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
case 8: |
/* UL ML */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
case 16: |
/* UL ML */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
binaryString.append(PENTBIT[29]); |
info("ML "); |
newtable = 4; |
break; |
} |
break; |
case 8: |
/* To PUNC */ |
switch (curtable) { |
case 1: |
/* ML PL */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
binaryString.append(PENTBIT[30]); |
info("PL "); |
newtable = 8; |
break; |
case 2: |
/* ML PL */ |
binaryString.append(PENTBIT[29]); |
info("ML "); |
binaryString.append(PENTBIT[30]); |
info("PL "); |
newtable = 8; |
break; |
case 4: |
/* PL */ |
binaryString.append(PENTBIT[30]); |
info("PL "); |
newtable = 8; |
break; |
case 16: |
/* UL ML PL */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
binaryString.append(PENTBIT[29]); |
info("ML "); |
binaryString.append(PENTBIT[30]); |
info("PL "); |
newtable = 8; |
break; |
} |
break; |
case 16: |
/* To DIGIT */ |
switch (curtable) { |
case 1: |
/* DL */ |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
case 2: |
/* DL */ |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
case 4: |
/* UL DL */ |
binaryString.append(PENTBIT[29]); |
info("UL "); |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
case 8: |
/* UL DL */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[30]); |
info("DL "); |
newtable = 16; |
break; |
} |
break; |
case 32: |
/* To BINARY */ |
lasttable = curtable; |
switch (curtable) { |
case 1: |
/* BS */ |
binaryString.append(PENTBIT[31]); |
info("BS "); |
newtable = 32; |
break; |
case 2: |
/* BS */ |
binaryString.append(PENTBIT[31]); |
info("BS "); |
newtable = 32; |
break; |
case 4: |
/* BS */ |
binaryString.append(PENTBIT[31]); |
info("BS "); |
newtable = 32; |
break; |
case 8: |
/* UL BS */ |
binaryString.append(PENTBIT[31]); |
info("UL "); |
binaryString.append(PENTBIT[31]); |
info("BS "); |
lasttable = 1; |
newtable = 32; |
break; |
case 16: |
/* UL BS */ |
binaryString.append(QUADBIT[14]); |
info("UL "); |
binaryString.append(PENTBIT[31]); |
info("BS "); |
lasttable = 1; |
newtable = 32; |
break; |
} |
bytes = 0; |
do { |
bytes++; |
} while (typemap[i + bytes - 1] == 32); |
bytes--; |
if (bytes > 2079) { |
throw new OkapiException("Input too long"); |
} |
if (bytes > 31) { |
/* Put 00000 followed by 11-bit number of bytes less 31 */ |
binaryString.append("00000"); |
for (int weight = 0x400; weight > 0; weight = weight >> 1) { |
if ((bytes - 31 & weight) != 0) { |
binaryString.append('1'); |
} else { |
binaryString.append('0'); |
} |
} |
} else { |
/* Put 5-bit number of bytes */ |
for (int weight = 0x10; weight > 0; weight = weight >> 1) { |
if ((bytes & weight) != 0) { |
binaryString.append('1'); |
} else { |
binaryString.append('0'); |
} |
} |
} |
break; |
} |
} |
} |
/* Add data to the binary string */ |
curtable = newtable; |
chartype = typemap[i]; |
if (chartype > 64) { |
chartype -= 64; |
} |
switch (chartype) { |
case 1: |
case 2: |
case 4: |
case 8: |
if (charmap[i] >= 400) { |
info("FLG(" + (charmap[i] - 400) + ") "); |
binaryString.append(TRIBIT[charmap[i] - 400]); |
if (charmap[i] != 400) { |
/* ECI */ |
binaryString.append(eciToBinary()); |
} |
} else { |
binaryString.append(PENTBIT[charmap[i]]); |
infoSpace(charmap[i]); |
} |
break; |
case 16: |
binaryString.append(QUADBIT[charmap[i]]); |
infoSpace(charmap[i]); |
break; |
case 32: |
for (int weight = 0x80; weight > 0; weight = weight >> 1) { |
if ((charmap[i] & weight) != 0) { |
binaryString.append('1'); |
} else { |
binaryString.append('0'); |
} |
} |
infoSpace(charmap[i]); |
break; |
} |
} |
infoLine(); |
return binaryString.toString(); |
} |
/** Adjusts bit stream so that no codewords are all 0s or all 1s, per Section 7.3.1.2 */ |
private StringBuilder adjustBinaryString(final String binaryString, final boolean compact, final int layers) { |
final StringBuilder adjustedString = new StringBuilder(); |
final int codewordSize = getCodewordSize(layers); |
int ones = 0; |
/* Insert dummy digits needed to prevent codewords of all 0s or all 1s */ |
for (int i = 0; i < binaryString.length(); i++) { |
if ((adjustedString.length() + 1) % codewordSize == 0) { |
if (ones == codewordSize - 1) { |
// codeword of B-1 1s, add dummy 0 |
adjustedString.append('0'); |
i--; |
} else if (ones == 0) { |
// codeword of B-1 0s, add dummy 1 |
adjustedString.append('1'); |
i--; |
} else { |
// no dummy value needed |
adjustedString.append(binaryString.charAt(i)); |
} |
ones = 0; |
} else { |
adjustedString.append(binaryString.charAt(i)); |
if (binaryString.charAt(i) == '1') { |
ones++; |
} |
} |
} |
/* Add padding */ |
int adjustedLength = adjustedString.length(); |
final int remainder = adjustedLength % codewordSize; |
int padBits = codewordSize - remainder; |
if (padBits == codewordSize) { |
padBits = 0; |
} |
for (int i = 0; i < padBits; i++) { |
adjustedString.append('1'); |
} |
adjustedLength = adjustedString.length(); |
/* Make sure padding didn't create an invalid (all 1s) codeword */ |
ones = 0; |
for (int i = adjustedLength - codewordSize; i < adjustedLength && i >= 0; i++) { |
if (adjustedString.charAt(i) == '1') { |
ones++; |
} |
} |
if (ones == codewordSize) { |
adjustedString.setCharAt(adjustedLength - 1, '0'); |
} |
/* Log the codewords */ |
info("Codewords: "); |
for (int i = 0; i < adjustedLength / codewordSize; i++) { |
int l = 0, m = 1 << codewordSize - 1; |
for (int j = 0; j < codewordSize; j++) { |
if (adjustedString.charAt(i * codewordSize + j) == '1') { |
l += m; |
} |
m = m >> 1; |
} |
infoSpace(l); |
} |
infoLine(); |
/* Return the adjusted bit string */ |
return adjustedString; |
} |
private String eciToBinary() { |
final String eciNumber = Integer.toString(this.eciMode); |
final StringBuilder binary = new StringBuilder(4 * eciNumber.length()); |
for (int i = 0; i < eciNumber.length(); i++) { |
binary.append(QUADBIT[eciNumber.charAt(i) - '0' + 2]); |
infoSpace(eciNumber.charAt(i)); |
} |
return binary.toString(); |
} |
/** Creates the descriptor / mode message, per Section 7.2 */ |
private String createDescriptor(final boolean compact, final int layers, final int dataBlocks) { |
final StringBuilder descriptor = new StringBuilder(); |
int descDataSize; |
if (compact) { |
/* The first 2 bits represent the number of layers minus 1 */ |
if ((layers - 1 & 0x02) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
if ((layers - 1 & 0x01) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
/* The next 6 bits represent the number of data blocks minus 1 */ |
if (this.readerInit) { |
descriptor.append('1'); |
} else { |
if ((dataBlocks - 1 & 0x20) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
for (int i = 0x10; i > 0; i = i >> 1) { |
if ((dataBlocks - 1 & i) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
descDataSize = 2; |
} else { |
/* The first 5 bits represent the number of layers minus 1 */ |
for (int i = 0x10; i > 0; i = i >> 1) { |
if ((layers - 1 & i) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
/* The next 11 bits represent the number of data blocks minus 1 */ |
if (this.readerInit) { |
descriptor.append('1'); |
} else { |
if ((dataBlocks - 1 & 0x400) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
for (int i = 0x200; i > 0; i = i >> 1) { |
if ((dataBlocks - 1 & i) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
descDataSize = 4; |
} |
infoLine("Mode Message: " + descriptor); |
/* Split into 4-bit codewords */ |
final int[] desc_data = new int[descDataSize]; |
for (int i = 0; i < descDataSize; i++) { |
for (int weight = 0; weight < 4; weight++) { |
if (descriptor.charAt(i * 4 + weight) == '1') { |
desc_data[i] += 8 >> weight; |
} |
} |
} |
/* |
* Add Reed-Solomon error correction with Galois Field GF(16) and prime modulus x^4 + x + 1 |
* (Section 7.2.3) |
*/ |
final ReedSolomon rs = new ReedSolomon(); |
rs.init_gf(0x13); |
if (compact) { |
rs.init_code(5, 1); |
rs.encode(2, desc_data); |
final int[] desc_ecc = new int[6]; |
for (int i = 0; i < 5; i++) { |
desc_ecc[i] = rs.getResult(i); |
} |
for (int i = 0; i < 5; i++) { |
for (int weight = 0x08; weight > 0; weight = weight >> 1) { |
if ((desc_ecc[4 - i] & weight) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
} |
} else { |
rs.init_code(6, 1); |
rs.encode(4, desc_data); |
final int[] desc_ecc = new int[6]; |
for (int i = 0; i < 6; i++) { |
desc_ecc[i] = rs.getResult(i); |
} |
for (int i = 0; i < 6; i++) { |
for (int weight = 0x08; weight > 0; weight = weight >> 1) { |
if ((desc_ecc[5 - i] & weight) != 0) { |
descriptor.append('1'); |
} else { |
descriptor.append('0'); |
} |
} |
} |
} |
return descriptor.toString(); |
} |
/** |
* Adds error correction data to the specified binary string, which already contains the primary |
* data |
*/ |
private void addErrorCorrection(final StringBuilder adjustedString, final int codewordSize, final int dataBlocks, final int eccBlocks) { |
int x, poly, startWeight; |
/* Split into codewords and calculate Reed-Solomon error correction codes */ |
switch (codewordSize) { |
case 6: |
x = 32; |
poly = 0x43; |
startWeight = 0x20; |
break; |
case 8: |
x = 128; |
poly = 0x12d; |
startWeight = 0x80; |
break; |
case 10: |
x = 512; |
poly = 0x409; |
startWeight = 0x200; |
break; |
case 12: |
x = 2048; |
poly = 0x1069; |
startWeight = 0x800; |
break; |
default: |
throw new OkapiException("Unrecognized codeword size: " + codewordSize); |
} |
final ReedSolomon rs = new ReedSolomon(); |
final int[] data = new int[dataBlocks + 3]; |
final int[] ecc = new int[eccBlocks + 3]; |
for (int i = 0; i < dataBlocks; i++) { |
for (int weight = 0; weight < codewordSize; weight++) { |
if (adjustedString.charAt(i * codewordSize + weight) == '1') { |
data[i] += x >> weight; |
} |
} |
} |
rs.init_gf(poly); |
rs.init_code(eccBlocks, 1); |
rs.encode(dataBlocks, data); |
for (int i = 0; i < eccBlocks; i++) { |
ecc[i] = rs.getResult(i); |
} |
for (int i = eccBlocks - 1; i >= 0; i--) { |
for (int weight = startWeight; weight > 0; weight = weight >> 1) { |
if ((ecc[i] & weight) != 0) { |
adjustedString.append('1'); |
} else { |
adjustedString.append('0'); |
} |
} |
} |
} |
/** Determines codeword bit length - Table 3 */ |
private static int getCodewordSize(final int layers) { |
if (layers >= 23) { |
return 12; |
} else if (layers >= 9 && layers <= 22) { |
return 10; |
} else if (layers >= 3 && layers <= 8) { |
return 8; |
} else { |
assert layers <= 2; |
return 6; |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Nve18.java |
---|
New file |
0,0 → 1,88 |
/* |
* Copyright 2015 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* <p> |
* Calculate NVE-18 (Nummer der Versandeinheit), also known as SSCC-18 (Serial Shipping Container |
* Code). |
* |
* <p> |
* Encodes a 17-digit number, adding a modulo-10 check digit. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Nve18 extends Symbol { |
@Override |
protected void encode() { |
String gs1Equivalent = ""; |
int zeroes; |
int count = 0; |
int c, cdigit; |
int p = 0; |
if (this.content.length() > 17) { |
throw new OkapiException("Input data too long"); |
} |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
// Add leading zeroes |
zeroes = 17 - this.content.length(); |
for (int i = 0; i < zeroes; i++) { |
gs1Equivalent += "0"; |
} |
gs1Equivalent += this.content; |
// Add Modulus-10 check digit |
for (int i = gs1Equivalent.length() - 1; i >= 0; i--) { |
c = Character.getNumericValue(gs1Equivalent.charAt(i)); |
if (p % 2 == 0) { |
c = c * 3; |
} |
count += c; |
p++; |
} |
cdigit = 10 - count % 10; |
if (cdigit == 10) { |
cdigit = 0; |
} |
infoLine("NVE Check Digit: " + cdigit); |
this.content = "[00]" + gs1Equivalent + cdigit; |
// Defer to Code 128 |
final Code128 code128 = new Code128(); |
code128.setDataType(DataType.GS1); |
code128.setHumanReadableLocation(this.humanReadableLocation); |
code128.setContent(this.content); |
this.readable = code128.readable; |
this.pattern = code128.pattern; |
this.row_count = code128.row_count; |
this.row_height = code128.row_height; |
this.symbol_height = code128.symbol_height; |
this.symbol_width = code128.symbol_width; |
this.rectangles = code128.rectangles; |
this.texts = code128.texts; |
info(code128.encodeInfo); |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/ChannelCode.java |
---|
New file |
0,0 → 1,167 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* <p> |
* Implements Channel Code according to ANSI/AIM BC12-1998. |
* |
* <p> |
* Channel Code encodes whole integer values between 0 and 7,742,862. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class ChannelCode extends Symbol { |
private int preferredNumberOfChannels; |
private final int[] space = new int[11]; |
private final int[] bar = new int[11]; |
private double currentValue; |
private double targetValue; |
/** |
* Sets the preferred number of channels used to encode data. This setting will be ignored if |
* the value to be encoded requires more channels. |
* |
* @param channels the preferred number of channels (3 to 8, inclusive) |
*/ |
public void setPreferredNumberOfChannels(final int channels) { |
if (channels < 3 || channels > 8) { |
throw new IllegalArgumentException("Invalid Channel Code number of channels: " + channels); |
} |
this.preferredNumberOfChannels = channels; |
} |
/** |
* Returns the preferred number of channels used to encode data. |
* |
* @return the preferred number of channels used to encode data |
*/ |
public int getPreferredNumberOfChannels() { |
return this.preferredNumberOfChannels; |
} |
@Override |
protected void encode() { |
int channels; |
int i; |
int leadingZeroCount; |
if (this.content.length() > 7) { |
throw new OkapiException("Input too long"); |
} |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
if (this.preferredNumberOfChannels <= 2 || this.preferredNumberOfChannels > 8) { |
channels = 3; |
} else { |
channels = this.preferredNumberOfChannels; |
} |
this.targetValue = Integer.parseInt(this.content); |
switch (channels) { |
case 3: |
if (this.targetValue > 26) { |
channels++; |
} |
case 4: |
if (this.targetValue > 292) { |
channels++; |
} |
case 5: |
if (this.targetValue > 3493) { |
channels++; |
} |
case 6: |
if (this.targetValue > 44072) { |
channels++; |
} |
case 7: |
if (this.targetValue > 576688) { |
channels++; |
} |
case 8: |
if (this.targetValue > 7742862) { |
channels++; |
} |
} |
if (channels == 9) { |
throw new OkapiException("Value out of range"); |
} |
infoLine("Channels Used: " + channels); |
for (i = 0; i < 11; i++) { |
this.bar[i] = 0; |
this.space[i] = 0; |
} |
this.bar[0] = this.space[1] = this.bar[1] = this.space[2] = this.bar[2] = 1; |
this.currentValue = 0; |
this.pattern = new String[1]; |
nextSpace(channels, 3, channels, channels); |
leadingZeroCount = channels - 1 - this.content.length(); |
this.readable = ""; |
for (i = 0; i < leadingZeroCount; i++) { |
this.readable += "0"; |
} |
this.readable += this.content; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
private void nextSpace(final int channels, final int i, final int maxSpace, final int maxBar) { |
for (int s = i < channels + 2 ? 1 : maxSpace; s <= maxSpace; s++) { |
this.space[i] = s; |
nextBar(channels, i, maxBar, maxSpace + 1 - s); |
} |
} |
private void nextBar(final int channels, final int i, final int maxBar, final int maxSpace) { |
int b = this.space[i] + this.bar[i - 1] + this.space[i - 1] + this.bar[i - 2] > 4 ? 1 : 2; |
if (i < channels + 2) { |
for (; b <= maxBar; b++) { |
this.bar[i] = b; |
nextSpace(channels, i + 1, maxSpace, maxBar + 1 - b); |
} |
} else if (b <= maxBar) { |
this.bar[i] = maxBar; |
checkIfDone(); |
this.currentValue++; |
} |
} |
private void checkIfDone() { |
if (this.currentValue == this.targetValue) { |
/* Target reached - save the generated pattern */ |
final StringBuilder sb = new StringBuilder(); |
sb.append("11110"); |
for (int i = 0; i < 11; i++) { |
sb.append((char) (this.space[i] + '0')); |
sb.append((char) (this.bar[i] + '0')); |
} |
this.pattern[0] = sb.toString(); |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/DataBar14.java |
---|
New file |
0,0 → 1,616 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.backend.DataBarLimited.getWidths; |
import java.math.BigInteger; |
/** |
* <p> |
* Implements GS1 DataBar Omnidirectional and GS1 DataBar Truncated according to ISO/IEC 24724:2011. |
* |
* <p> |
* Input data should be a 13-digit Global Trade Identification Number (GTIN) without check digit or |
* Application Identifier [01]. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class DataBar14 extends Symbol { |
public enum Mode { |
/** DataBar-14 */ |
LINEAR, |
/** DataBar-14 Omnidirectional */ |
OMNI, |
/** DataBar-14 Omnidirectional Stacked */ |
STACKED |
} |
private static final int[] G_SUM_TABLE = { 0, 161, 961, 2015, 2715, 0, 336, 1036, 1516 }; |
private static final int[] T_TABLE = { 1, 10, 34, 70, 126, 4, 20, 48, 81 }; |
private static final int[] MODULES_ODD = { 12, 10, 8, 6, 4, 5, 7, 9, 11 }; |
private static final int[] MODULES_EVEN = { 4, 6, 8, 10, 12, 10, 8, 6, 4 }; |
private static final int[] WIDEST_ODD = { 8, 6, 4, 3, 1, 2, 4, 6, 8 }; |
private static final int[] WIDEST_EVEN = { 1, 3, 5, 6, 8, 7, 5, 3, 1 }; |
private static final int[] CHECKSUM_WEIGHT = { /* Table 5 */ |
1, 3, 9, 27, 2, 6, 18, 54, 4, 12, 36, 29, 8, 24, 72, 58, 16, 48, 65, 37, 32, 17, 51, 74, 64, 34, 23, 69, 49, 68, 46, 59 }; |
private static final int[] FINDER_PATTERN = { 3, 8, 2, 1, 1, 3, 5, 5, 1, 1, 3, 3, 7, 1, 1, 3, 1, 9, 1, 1, 2, 7, 4, 1, 1, 2, 5, 6, 1, 1, 2, 3, 8, 1, 1, 1, 5, 7, 1, 1, 1, 3, 9, 1, 1 }; |
private boolean linkageFlag; |
private Mode mode = Mode.LINEAR; |
@Override |
public void setDataType(final DataType dummy) { |
// Do nothing! |
} |
/** |
* Although this is a GS1 symbology, input data is expected to omit the [01] Application |
* Identifier, as well as the check digit. Thus, the input data is not considered GS1-format |
* data. |
*/ |
@Override |
protected boolean gs1Supported() { |
return false; |
} |
protected void setLinkageFlag(final boolean linkageFlag) { |
this.linkageFlag = linkageFlag; |
} |
protected boolean getLinkageFlag() { |
return this.linkageFlag; |
} |
/** |
* Sets the symbol mode. The default is {@link Mode#LINEAR}. |
* |
* @param mode the symbol mode |
*/ |
public void setMode(final Mode mode) { |
this.mode = mode; |
} |
/** |
* Returns the symbol mode. |
* |
* @return the symbol mode |
*/ |
public Mode getMode() { |
return this.mode; |
} |
@Override |
protected void encode() { |
final boolean[][] grid = new boolean[5][100]; |
BigInteger accum; |
BigInteger left_reg; |
BigInteger right_reg; |
final int[] data_character = new int[4]; |
final int[] data_group = new int[4]; |
final int[] v_odd = new int[4]; |
final int[] v_even = new int[4]; |
int i; |
final int[][] data_widths = new int[8][4]; |
int checksum; |
int c_left; |
int c_right; |
final int[] total_widths = new int[46]; |
int writer; |
char latch; |
int j; |
int count; |
int check_digit; |
final StringBuilder bin = new StringBuilder(); |
int compositeOffset = 0; |
if (this.content.length() > 13) { |
throw new OkapiException("Input too long"); |
} |
if (!this.content.matches("[0-9]+?")) { |
throw new OkapiException("Invalid characters in input"); |
} |
accum = new BigInteger(this.content); |
if (this.linkageFlag) { |
accum = accum.add(new BigInteger("10000000000000")); |
compositeOffset = 1; |
} |
/* Calculate left and right pair values */ |
left_reg = accum.divide(new BigInteger("4537077")); |
right_reg = accum.mod(new BigInteger("4537077")); |
/* Calculate four data characters */ |
accum = left_reg.divide(new BigInteger("1597")); |
data_character[0] = accum.intValue(); |
accum = left_reg.mod(new BigInteger("1597")); |
data_character[1] = accum.intValue(); |
accum = right_reg.divide(new BigInteger("1597")); |
data_character[2] = accum.intValue(); |
accum = right_reg.mod(new BigInteger("1597")); |
data_character[3] = accum.intValue(); |
info("Data Characters: "); |
for (i = 0; i < 4; i++) { |
infoSpace(data_character[i]); |
} |
infoLine(); |
/* Calculate odd and even subset values */ |
if (data_character[0] >= 0 && data_character[0] <= 160) { |
data_group[0] = 0; |
} |
if (data_character[0] >= 161 && data_character[0] <= 960) { |
data_group[0] = 1; |
} |
if (data_character[0] >= 961 && data_character[0] <= 2014) { |
data_group[0] = 2; |
} |
if (data_character[0] >= 2015 && data_character[0] <= 2714) { |
data_group[0] = 3; |
} |
if (data_character[0] >= 2715 && data_character[0] <= 2840) { |
data_group[0] = 4; |
} |
if (data_character[1] >= 0 && data_character[1] <= 335) { |
data_group[1] = 5; |
} |
if (data_character[1] >= 336 && data_character[1] <= 1035) { |
data_group[1] = 6; |
} |
if (data_character[1] >= 1036 && data_character[1] <= 1515) { |
data_group[1] = 7; |
} |
if (data_character[1] >= 1516 && data_character[1] <= 1596) { |
data_group[1] = 8; |
} |
if (data_character[3] >= 0 && data_character[3] <= 335) { |
data_group[3] = 5; |
} |
if (data_character[3] >= 336 && data_character[3] <= 1035) { |
data_group[3] = 6; |
} |
if (data_character[3] >= 1036 && data_character[3] <= 1515) { |
data_group[3] = 7; |
} |
if (data_character[3] >= 1516 && data_character[3] <= 1596) { |
data_group[3] = 8; |
} |
if (data_character[2] >= 0 && data_character[2] <= 160) { |
data_group[2] = 0; |
} |
if (data_character[2] >= 161 && data_character[2] <= 960) { |
data_group[2] = 1; |
} |
if (data_character[2] >= 961 && data_character[2] <= 2014) { |
data_group[2] = 2; |
} |
if (data_character[2] >= 2015 && data_character[2] <= 2714) { |
data_group[2] = 3; |
} |
if (data_character[2] >= 2715 && data_character[2] <= 2840) { |
data_group[2] = 4; |
} |
v_odd[0] = (data_character[0] - G_SUM_TABLE[data_group[0]]) / T_TABLE[data_group[0]]; |
v_even[0] = (data_character[0] - G_SUM_TABLE[data_group[0]]) % T_TABLE[data_group[0]]; |
v_odd[1] = (data_character[1] - G_SUM_TABLE[data_group[1]]) % T_TABLE[data_group[1]]; |
v_even[1] = (data_character[1] - G_SUM_TABLE[data_group[1]]) / T_TABLE[data_group[1]]; |
v_odd[3] = (data_character[3] - G_SUM_TABLE[data_group[3]]) % T_TABLE[data_group[3]]; |
v_even[3] = (data_character[3] - G_SUM_TABLE[data_group[3]]) / T_TABLE[data_group[3]]; |
v_odd[2] = (data_character[2] - G_SUM_TABLE[data_group[2]]) / T_TABLE[data_group[2]]; |
v_even[2] = (data_character[2] - G_SUM_TABLE[data_group[2]]) % T_TABLE[data_group[2]]; |
/* Use RSS subset width algorithm */ |
for (i = 0; i < 4; i++) { |
if (i == 0 || i == 2) { |
int[] widths = getWidths(v_odd[i], MODULES_ODD[data_group[i]], 4, WIDEST_ODD[data_group[i]], 1); |
data_widths[0][i] = widths[0]; |
data_widths[2][i] = widths[1]; |
data_widths[4][i] = widths[2]; |
data_widths[6][i] = widths[3]; |
widths = getWidths(v_even[i], MODULES_EVEN[data_group[i]], 4, WIDEST_EVEN[data_group[i]], 0); |
data_widths[1][i] = widths[0]; |
data_widths[3][i] = widths[1]; |
data_widths[5][i] = widths[2]; |
data_widths[7][i] = widths[3]; |
} else { |
int[] widths = getWidths(v_odd[i], MODULES_ODD[data_group[i]], 4, WIDEST_ODD[data_group[i]], 0); |
data_widths[0][i] = widths[0]; |
data_widths[2][i] = widths[1]; |
data_widths[4][i] = widths[2]; |
data_widths[6][i] = widths[3]; |
widths = getWidths(v_even[i], MODULES_EVEN[data_group[i]], 4, WIDEST_EVEN[data_group[i]], 1); |
data_widths[1][i] = widths[0]; |
data_widths[3][i] = widths[1]; |
data_widths[5][i] = widths[2]; |
data_widths[7][i] = widths[3]; |
} |
} |
/* Calculate the checksum */ |
checksum = 0; |
for (i = 0; i < 8; i++) { |
checksum += CHECKSUM_WEIGHT[i] * data_widths[i][0]; |
checksum += CHECKSUM_WEIGHT[i + 8] * data_widths[i][1]; |
checksum += CHECKSUM_WEIGHT[i + 16] * data_widths[i][2]; |
checksum += CHECKSUM_WEIGHT[i + 24] * data_widths[i][3]; |
} |
checksum %= 79; |
/* Calculate the two check characters */ |
if (checksum >= 8) { |
checksum++; |
} |
if (checksum >= 72) { |
checksum++; |
} |
c_left = checksum / 9; |
c_right = checksum % 9; |
infoLine("Checksum: " + checksum); |
/* Put element widths together */ |
total_widths[0] = 1; |
total_widths[1] = 1; |
total_widths[44] = 1; |
total_widths[45] = 1; |
for (i = 0; i < 8; i++) { |
total_widths[i + 2] = data_widths[i][0]; |
total_widths[i + 15] = data_widths[7 - i][1]; |
total_widths[i + 23] = data_widths[i][3]; |
total_widths[i + 36] = data_widths[7 - i][2]; |
} |
for (i = 0; i < 5; i++) { |
total_widths[i + 10] = FINDER_PATTERN[i + 5 * c_left]; |
total_widths[i + 31] = FINDER_PATTERN[4 - i + 5 * c_right]; |
} |
this.row_count = 0; |
final boolean[] separator = new boolean[100]; |
for (i = 0; i < separator.length; i++) { |
separator[i] = false; |
} |
/* Put this data into the symbol */ |
if (this.mode == Mode.LINEAR) { |
writer = 0; |
latch = '0'; |
for (i = 0; i < 46; i++) { |
for (j = 0; j < total_widths[i]; j++) { |
if (latch == '1') { |
grid[this.row_count][writer] = true; |
} |
writer++; |
} |
if (latch == '1') { |
latch = '0'; |
} else { |
latch = '1'; |
} |
} |
if (this.symbol_width < writer) { |
this.symbol_width = writer; |
} |
if (this.linkageFlag) { |
/* separator pattern for composite symbol */ |
for (i = 4; i < 92; i++) { |
separator[i] = !grid[0][i]; |
} |
latch = '1'; |
for (i = 16; i < 32; i++) { |
if (!grid[0][i]) { |
if (latch == '1') { |
separator[i] = true; |
latch = '0'; |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} |
latch = '1'; |
for (i = 63; i < 78; i++) { |
if (!grid[0][i]) { |
if (latch == '1') { |
separator[i] = true; |
latch = '0'; |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} |
} |
this.row_count = this.row_count + 1; |
count = 0; |
check_digit = 0; |
/* Calculate check digit from Annex A and place human readable text */ |
final StringBuilder hrt = new StringBuilder(14); |
for (i = this.content.length(); i < 13; i++) { |
hrt.append('0'); |
} |
hrt.append(this.content); |
for (i = 0; i < 13; i++) { |
count += hrt.charAt(i) - '0'; |
if ((i & 1) == 0) { |
count += 2 * (hrt.charAt(i) - '0'); |
} |
} |
check_digit = 10 - count % 10; |
if (check_digit == 10) { |
check_digit = 0; |
} |
infoLine("Check Digit: " + check_digit); |
hrt.append((char) (check_digit + '0')); |
this.readable = "(01)" + hrt; |
} |
if (this.mode == Mode.STACKED) { |
/* top row */ |
writer = 0; |
latch = '0'; |
for (i = 0; i < 23; i++) { |
for (j = 0; j < total_widths[i]; j++) { |
grid[this.row_count][writer] = latch == '1'; |
writer++; |
} |
if (latch == '1') { |
latch = '0'; |
} else { |
latch = '1'; |
} |
} |
grid[this.row_count][writer] = true; |
grid[this.row_count][writer + 1] = false; |
/* bottom row */ |
this.row_count = this.row_count + 2; |
grid[this.row_count][0] = true; |
grid[this.row_count][1] = false; |
writer = 0; |
latch = '1'; |
for (i = 23; i < 46; i++) { |
for (j = 0; j < total_widths[i]; j++) { |
grid[this.row_count][writer + 2] = latch == '1'; |
writer++; |
} |
if (latch == '1') { |
latch = '0'; |
} else { |
latch = '1'; |
} |
} |
/* separator pattern */ |
for (i = 1; i < 46; i++) { |
if (grid[this.row_count - 2][i] == grid[this.row_count][i]) { |
if (!grid[this.row_count - 2][i]) { |
grid[this.row_count - 1][i] = true; |
} |
} else { |
if (!grid[this.row_count - 1][i - 1]) { |
grid[this.row_count - 1][i] = true; |
} |
} |
} |
for (i = 0; i < 4; i++) { |
grid[this.row_count - 1][i] = false; |
} |
if (this.linkageFlag) { |
/* separator pattern for composite symbol */ |
for (i = 4; i < 46; i++) { |
separator[i] = !grid[0][i]; |
} |
latch = '1'; |
for (i = 16; i < 32; i++) { |
if (!grid[0][i]) { |
if (latch == '1') { |
separator[i] = true; |
latch = '0'; |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} |
} |
this.row_count = this.row_count + 1; |
if (this.symbol_width < 50) { |
this.symbol_width = 50; |
} |
} |
if (this.mode == Mode.OMNI) { |
/* top row */ |
writer = 0; |
latch = '0'; |
for (i = 0; i < 23; i++) { |
for (j = 0; j < total_widths[i]; j++) { |
grid[this.row_count][writer] = latch == '1'; |
writer++; |
} |
latch = latch == '1' ? '0' : '1'; |
} |
grid[this.row_count][writer] = true; |
grid[this.row_count][writer + 1] = false; |
/* bottom row */ |
this.row_count = this.row_count + 4; |
grid[this.row_count][0] = true; |
grid[this.row_count][1] = false; |
writer = 0; |
latch = '1'; |
for (i = 23; i < 46; i++) { |
for (j = 0; j < total_widths[i]; j++) { |
grid[this.row_count][writer + 2] = latch == '1'; |
writer++; |
} |
if (latch == '1') { |
latch = '0'; |
} else { |
latch = '1'; |
} |
} |
/* middle separator */ |
for (i = 5; i < 46; i += 2) { |
grid[this.row_count - 2][i] = true; |
} |
/* top separator */ |
for (i = 4; i < 46; i++) { |
if (!grid[this.row_count - 4][i]) { |
grid[this.row_count - 3][i] = true; |
} |
} |
latch = '1'; |
for (i = 17; i < 33; i++) { |
if (!grid[this.row_count - 4][i]) { |
if (latch == '1') { |
grid[this.row_count - 3][i] = true; |
latch = '0'; |
} else { |
grid[this.row_count - 3][i] = false; |
latch = '1'; |
} |
} else { |
grid[this.row_count - 3][i] = false; |
latch = '1'; |
} |
} |
/* bottom separator */ |
for (i = 4; i < 46; i++) { |
if (!grid[this.row_count][i]) { |
grid[this.row_count - 1][i] = true; |
} |
} |
latch = '1'; |
for (i = 16; i < 32; i++) { |
if (!grid[this.row_count][i]) { |
if (latch == '1') { |
grid[this.row_count - 1][i] = true; |
latch = '0'; |
} else { |
grid[this.row_count - 1][i] = false; |
latch = '1'; |
} |
} else { |
grid[this.row_count - 1][i] = false; |
latch = '1'; |
} |
} |
if (this.symbol_width < 50) { |
this.symbol_width = 50; |
} |
if (this.linkageFlag) { |
/* separator pattern for composite symbol */ |
for (i = 4; i < 46; i++) { |
separator[i] = !grid[0][i]; |
} |
latch = '1'; |
for (i = 16; i < 32; i++) { |
if (!grid[0][i]) { |
if (latch == '1') { |
separator[i] = true; |
latch = '0'; |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} else { |
separator[i] = false; |
latch = '1'; |
} |
} |
} |
this.row_count = this.row_count + 1; |
} |
this.pattern = new String[this.row_count + compositeOffset]; |
this.row_height = new int[this.row_count + compositeOffset]; |
if (this.linkageFlag) { |
bin.setLength(0); |
for (j = 0; j < this.symbol_width; j++) { |
if (separator[j]) { |
bin.append('1'); |
} else { |
bin.append('0'); |
} |
} |
this.pattern[0] = bin2pat(bin); |
this.row_height[0] = 1; |
} |
for (i = 0; i < this.row_count; i++) { |
bin.setLength(0); |
for (j = 0; j < this.symbol_width; j++) { |
if (grid[i][j]) { |
bin.append('1'); |
} else { |
bin.append('0'); |
} |
} |
this.pattern[i + compositeOffset] = bin2pat(bin); |
} |
if (this.mode == Mode.LINEAR) { |
this.row_height[0 + compositeOffset] = -1; |
} |
if (this.mode == Mode.STACKED) { |
this.row_height[0 + compositeOffset] = 5; |
this.row_height[1 + compositeOffset] = 1; |
this.row_height[2 + compositeOffset] = 7; |
} |
if (this.mode == Mode.OMNI) { |
this.row_height[0 + compositeOffset] = -1; |
this.row_height[1 + compositeOffset] = 1; |
this.row_height[2 + compositeOffset] = 1; |
this.row_height[3 + compositeOffset] = 1; |
this.row_height[4 + compositeOffset] = -1; |
} |
if (this.linkageFlag) { |
this.row_count++; |
} |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Ean.java |
---|
New file |
0,0 → 1,352 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.backend.HumanReadableLocation.BOTTOM; |
import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE; |
import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP; |
import java.awt.geom.Rectangle2D; |
/** |
* <p> |
* Implements EAN bar code symbology according to BS EN 797:1996. |
* |
* <p> |
* European Article Number data can be encoded in EAN-8 or EAN-13 format requiring a 7-digit or |
* 12-digit input respectively. EAN-13 numbers map to Global Trade Identification Numbers (GTIN) |
* whereas EAN-8 symbols are generally for internal use only. Check digit is calculated and should |
* not be in input data. Leading zeroes are added as required. |
* |
* <p> |
* Add-on content can be appended to the main symbol content by adding a <tt>'+'</tt> character, |
* followed by the add-on content (up to 5 digits). |
* |
* @author <a href="mailto:jakel2006@me.com">Robert Elliott</a> |
*/ |
public class Ean extends Symbol { |
public enum Mode { |
EAN8, EAN13 |
}; |
private static final String[] EAN13_PARITY = { "AAAAAA", "AABABB", "AABBAB", "AABBBA", "ABAABB", "ABBAAB", "ABBBAA", "ABABAB", "ABABBA", "ABBABA" }; |
private static final String[] EAN_SET_A = { "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213", "3112" }; |
private static final String[] EAN_SET_B = { "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121", "2113" }; |
private Mode mode = Mode.EAN13; |
private int guardPatternExtraHeight = 5; |
private boolean linkageFlag; |
private EanUpcAddOn addOn; |
/** Creates a new instance. */ |
public Ean() { |
this.humanReadableAlignment = HumanReadableAlignment.JUSTIFY; |
} |
/** |
* Sets the EAN mode (EAN-8 or EAN-13). The default is EAN-13. |
* |
* @param mode the EAN mode (EAN-8 or EAN-13) |
*/ |
public void setMode(final Mode mode) { |
this.mode = mode; |
} |
/** |
* Returns the EAN mode (EAN-8 or EAN-13). |
* |
* @return the EAN mode (EAN-8 or EAN-13) |
*/ |
public Mode getMode() { |
return this.mode; |
} |
/** |
* Sets the extra height used for the guard patterns. The default value is <code>5</code>. |
* |
* @param guardPatternExtraHeight the extra height used for the guard patterns |
*/ |
public void setGuardPatternExtraHeight(final int guardPatternExtraHeight) { |
this.guardPatternExtraHeight = guardPatternExtraHeight; |
} |
/** |
* Returns the extra height used for the guard patterns. |
* |
* @return the extra height used for the guard patterns |
*/ |
public int getGuardPatternExtraHeight() { |
return this.guardPatternExtraHeight; |
} |
/** |
* Sets the linkage flag. If set to <code>true</code>, this symbol is part of a composite |
* symbol. |
* |
* @param linkageFlag the linkage flag |
*/ |
protected void setLinkageFlag(final boolean linkageFlag) { |
this.linkageFlag = linkageFlag; |
} |
@Override |
protected void encode() { |
separateContent(); |
if (this.content.isEmpty()) { |
throw new OkapiException("Missing EAN data"); |
} |
if (this.mode == Mode.EAN8) { |
ean8(); |
} else { |
ean13(); |
} |
} |
private void separateContent() { |
final int splitPoint = this.content.indexOf('+'); |
if (splitPoint == -1) { |
// there is no add-on data |
this.addOn = null; |
} else if (splitPoint == this.content.length() - 1) { |
// we found the add-on separator, but no add-on data |
throw new OkapiException("Invalid add-on data"); |
} else { |
// there is a '+' in the input data, use an add-on EAN2 or EAN5 |
this.addOn = new EanUpcAddOn(); |
this.addOn.font = this.font; |
this.addOn.fontName = this.fontName; |
this.addOn.fontSize = this.fontSize; |
this.addOn.humanReadableLocation = this.humanReadableLocation == NONE ? NONE : TOP; |
this.addOn.moduleWidth = this.moduleWidth; |
this.addOn.default_height = this.default_height + this.guardPatternExtraHeight - 8; |
this.addOn.setContent(this.content.substring(splitPoint + 1)); |
this.content = this.content.substring(0, splitPoint); |
} |
} |
private void ean13() { |
this.content = validateAndPad(this.content, 12); |
final char check = calcDigit(this.content); |
infoLine("Check Digit: " + check); |
final String hrt = this.content + check; |
final char parityChar = hrt.charAt(0); |
final String parity = EAN13_PARITY[parityChar - '0']; |
infoLine("Parity Digit: " + parityChar); |
final StringBuilder dest = new StringBuilder("111"); |
for (int i = 1; i < 13; i++) { |
if (i == 7) { |
dest.append("11111"); |
} |
if (i <= 6) { |
if (parity.charAt(i - 1) == 'B') { |
dest.append(EAN_SET_B[hrt.charAt(i) - '0']); |
} else { |
dest.append(EAN_SET_A[hrt.charAt(i) - '0']); |
} |
} else { |
dest.append(EAN_SET_A[hrt.charAt(i) - '0']); |
} |
} |
dest.append("111"); |
this.readable = hrt; |
this.pattern = new String[] { dest.toString() }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
private void ean8() { |
this.content = validateAndPad(this.content, 7); |
final char check = calcDigit(this.content); |
infoLine("Check Digit: " + check); |
final String hrt = this.content + check; |
final StringBuilder dest = new StringBuilder("111"); |
for (int i = 0; i < 8; i++) { |
if (i == 4) { |
dest.append("11111"); |
} |
dest.append(EAN_SET_A[hrt.charAt(i) - '0']); |
} |
dest.append("111"); |
this.readable = hrt; |
this.pattern = new String[] { dest.toString() }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
protected static String validateAndPad(String s, final int targetLength) { |
if (!s.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
if (s.length() > targetLength) { |
throw new OkapiException("Input data too long"); |
} |
if (s.length() < targetLength) { |
for (int i = s.length(); i < targetLength; i++) { |
s = '0' + s; |
} |
} |
return s; |
} |
public static char calcDigit(final String s) { |
int count = 0; |
int p = 0; |
for (int i = s.length() - 1; i >= 0; i--) { |
int c = Character.getNumericValue(s.charAt(i)); |
if (p % 2 == 0) { |
c = c * 3; |
} |
count += c; |
p++; |
} |
int cdigit = 10 - count % 10; |
if (cdigit == 10) { |
cdigit = 0; |
} |
return (char) (cdigit + '0'); |
} |
@Override |
protected void plotSymbol() { |
int xBlock; |
int x, y, w, h; |
boolean black = true; |
final int compositeOffset = this.linkageFlag ? 6 : 0; // space for composite separator above |
final int hrtOffset = this.humanReadableLocation == TOP ? getTheoreticalHumanReadableHeight() : 0; // space |
// for |
// HRT |
// above |
this.rectangles.clear(); |
this.texts.clear(); |
x = 0; |
/* Draw the bars in the symbology */ |
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) { |
w = this.pattern[0].charAt(xBlock) - '0'; |
if (black) { |
y = 0; |
h = this.default_height; |
/* Add extension to guide bars */ |
if (this.mode == Mode.EAN13) { |
if (x < 3 || x > 91 || x > 45 && x < 49) { |
h += this.guardPatternExtraHeight; |
} |
if (this.linkageFlag && (x == 0 || x == 94)) { |
h += 2; |
y -= 2; |
} |
} else { |
if (x < 3 || x > 62 || x > 30 && x < 35) { |
h += this.guardPatternExtraHeight; |
} |
if (this.linkageFlag && (x == 0 || x == 66)) { |
h += 2; |
y -= 2; |
} |
} |
final Rectangle2D.Double rect = new Rectangle2D.Double(scale(x), y + compositeOffset + hrtOffset, scale(w), h); |
this.rectangles.add(rect); |
this.symbol_width = Math.max(this.symbol_width, (int) rect.getMaxX()); |
this.symbol_height = Math.max(this.symbol_height, (int) rect.getHeight()); |
} |
black = !black; |
x += w; |
} |
/* Add separator for composite symbology, if necessary */ |
if (this.linkageFlag) { |
if (this.mode == Mode.EAN13) { |
this.rectangles.add(new Rectangle2D.Double(scale(0), 0, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(94), 0, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(-1), 2, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(95), 2, scale(1), 2)); |
} else { // EAN8 |
this.rectangles.add(new Rectangle2D.Double(scale(0), 0, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(66), 0, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(-1), 2, scale(1), 2)); |
this.rectangles.add(new Rectangle2D.Double(scale(67), 2, scale(1), 2)); |
} |
this.symbol_height += 4; |
} |
/* Now add the text */ |
if (this.humanReadableLocation == BOTTOM) { |
this.symbol_height -= this.guardPatternExtraHeight; |
final double baseline = this.symbol_height + this.fontSize; |
if (this.mode == Mode.EAN13) { |
this.texts.add(new TextBox(scale(-9), baseline, scale(4), this.readable.substring(0, 1), HumanReadableAlignment.RIGHT)); |
this.texts.add(new TextBox(scale(5), baseline, scale(39), this.readable.substring(1, 7), this.humanReadableAlignment)); |
this.texts.add(new TextBox(scale(51), baseline, scale(39), this.readable.substring(7, 13), this.humanReadableAlignment)); |
} else { // EAN8 |
this.texts.add(new TextBox(scale(5), baseline, scale(25), this.readable.substring(0, 4), this.humanReadableAlignment)); |
this.texts.add(new TextBox(scale(37), baseline, scale(25), this.readable.substring(4, 8), this.humanReadableAlignment)); |
} |
} else if (this.humanReadableLocation == TOP) { |
final double baseline = this.fontSize; |
final int width = this.mode == Mode.EAN13 ? 94 : 66; |
this.texts.add(new TextBox(scale(0), baseline, scale(width), this.readable, this.humanReadableAlignment)); |
} |
/* Now add the add-on symbol, if necessary */ |
if (this.addOn != null) { |
final int gap = 9; |
final int baseX = this.symbol_width + scale(gap); |
final Rectangle2D.Double r1 = this.rectangles.get(0); |
final Rectangle2D.Double ar1 = this.addOn.rectangles.get(0); |
final int baseY = (int) (r1.y + r1.getHeight() - ar1.y - ar1.getHeight()); |
for (final TextBox t : this.addOn.getTexts()) { |
this.texts.add(new TextBox(baseX + t.x, baseY + t.y, t.width, t.text, t.alignment)); |
} |
for (final Rectangle2D.Double r : this.addOn.getRectangles()) { |
this.rectangles.add(new Rectangle2D.Double(baseX + r.x, baseY + r.y, r.width, r.height)); |
} |
this.symbol_width += scale(gap) + this.addOn.symbol_width; |
this.pattern[0] = this.pattern[0] + gap + this.addOn.pattern[0]; |
} |
} |
/** Scales the specified width or x-dimension according to the current module width. */ |
private int scale(final int w) { |
return this.moduleWidth * w; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Logmars.java |
---|
New file |
0,0 → 1,98 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import static uk.org.okapibarcode.util.Arrays.positionOf; |
/** |
* Implements the LOGMARS (Logistics Applications of Automated Marking and Reading Symbols) standard |
* used by the US Department of Defense. Input data can be of any length and supports the characters |
* 0-9, A-Z, dash (-), full stop (.), space, dollar ($), slash (/), plus (+) and percent (%). A |
* Modulo-43 check digit is calculated and added, and should not form part of the input data. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Logmars extends Symbol { |
private static final String[] CODE39LM = { "1113313111", "3113111131", "1133111131", "3133111111", "1113311131", "3113311111", "1133311111", "1113113131", "3113113111", "1133113111", "3111131131", |
"1131131131", "3131131111", "1111331131", "3111331111", "1131331111", "1111133131", "3111133111", "1131133111", "1111333111", "3111111331", "1131111331", "3131111311", "1111311331", |
"3111311311", "1131311311", "1111113331", "3111113311", "1131113311", "1111313311", "3311111131", "1331111131", "3331111111", "1311311131", "3311311111", "1331311111", "1311113131", |
"3311113111", "1331113111", "1313131111", "1313111311", "1311131311", "1113131311" }; |
private static final char[] LOOKUP = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', |
'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%' }; |
/** Ratio of wide bar width to narrow bar width. */ |
private double moduleWidthRatio = 3; |
/** |
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually between |
* {@code 2} and {@code 3}. The default value is {@code 3}. |
* |
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width |
*/ |
public void setModuleWidthRatio(final double moduleWidthRatio) { |
this.moduleWidthRatio = moduleWidthRatio; |
} |
/** |
* Returns the ratio of wide bar width to narrow bar width. |
* |
* @return the ratio of wide bar width to narrow bar width |
*/ |
public double getModuleWidthRatio() { |
return this.moduleWidthRatio; |
} |
/** {@inheritDoc} */ |
@Override |
protected double getModuleWidth(final int originalWidth) { |
if (originalWidth == 1) { |
return 1; |
} else { |
return this.moduleWidthRatio; |
} |
} |
/** {@inheritDoc} */ |
@Override |
protected void encode() { |
if (!this.content.matches("[0-9A-Z\\. \\-$/+%]*")) { |
throw new OkapiException("Invalid characters in input"); |
} |
String p = ""; |
final int l = this.content.length(); |
int charval, counter = 0; |
char thischar; |
char checkDigit; |
for (int i = 0; i < l; i++) { |
thischar = this.content.charAt(i); |
charval = positionOf(thischar, LOOKUP); |
counter += charval; |
p += CODE39LM[charval]; |
} |
counter = counter % 43; |
checkDigit = LOOKUP[counter]; |
infoLine("Check Digit: " + checkDigit); |
p += CODE39LM[counter]; |
this.readable = this.content + checkDigit; |
this.pattern = new String[] { "1311313111" + p + "131131311" }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/KoreaPost.java |
---|
New file |
0,0 → 1,64 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
/** |
* <p> |
* Implements Korea Post Barcode. Input should consist of of a six-digit number. A Modulo-10 check |
* digit is calculated and added, and should not form part of the input data. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class KoreaPost extends Symbol { |
private static final String[] KOREA_TABLE = { "1313150613", "0713131313", "0417131313", "1506131313", "0413171313", "17171313", "1315061313", "0413131713", "17131713", "13171713" }; |
@Override |
protected void encode() { |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in input"); |
} |
if (this.content.length() > 6) { |
throw new OkapiException("Input data too long"); |
} |
String padded = ""; |
for (int i = 0; i < 6 - this.content.length(); i++) { |
padded += "0"; |
} |
padded += this.content; |
int total = 0; |
String accumulator = ""; |
for (int i = 0; i < padded.length(); i++) { |
final int j = Character.getNumericValue(padded.charAt(i)); |
accumulator += KOREA_TABLE[j]; |
total += j; |
} |
int checkd = 10 - total % 10; |
if (checkd == 10) { |
checkd = 0; |
} |
infoLine("Check Digit: " + checkd); |
accumulator += KOREA_TABLE[checkd]; |
this.readable = padded + checkd; |
this.pattern = new String[] { accumulator }; |
this.row_count = 1; |
this.row_height = new int[] { -1 }; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Pharmacode2Track.java |
---|
New file |
0,0 → 1,117 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import java.awt.geom.Rectangle2D; |
/** |
* Implements the Two-Track Pharmacode bar code symbology. <br> |
* Pharmacode Two-Track is an alternative system to Pharmacode One-Track used for the identification |
* of pharmaceuticals. The symbology is able to encode whole numbers between 4 and 64570080. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class Pharmacode2Track extends Symbol { |
@Override |
protected void encode() { |
int i, tester = 0; |
String inter, dest; |
if (this.content.length() > 8) { |
throw new OkapiException("Input too long"); |
} |
if (!this.content.matches("[0-9]+")) { |
throw new OkapiException("Invalid characters in data"); |
} |
for (i = 0; i < this.content.length(); i++) { |
tester *= 10; |
tester += Character.getNumericValue(this.content.charAt(i)); |
} |
if (tester < 4 || tester > 64570080) { |
throw new OkapiException("Data out of range"); |
} |
inter = ""; |
do { |
switch (tester % 3) { |
case 0: |
inter += "F"; |
tester = (tester - 3) / 3; |
break; |
case 1: |
inter += "D"; |
tester = (tester - 1) / 3; |
break; |
case 2: |
inter += "A"; |
tester = (tester - 2) / 3; |
break; |
} |
} while (tester != 0); |
dest = ""; |
for (i = inter.length() - 1; i >= 0; i--) { |
dest += inter.charAt(i); |
} |
infoLine("Encoding: " + dest); |
this.readable = ""; |
this.pattern = new String[1]; |
this.pattern[0] = dest; |
this.row_count = 1; |
this.row_height = new int[1]; |
this.row_height[0] = -1; |
} |
@Override |
protected void plotSymbol() { |
int xBlock; |
int x, y, w, h; |
this.rectangles.clear(); |
x = 0; |
w = 1; |
y = 0; |
h = 0; |
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) { |
switch (this.pattern[0].charAt(xBlock)) { |
case 'A': |
y = 0; |
h = this.default_height / 2; |
break; |
case 'D': |
y = this.default_height / 2; |
h = this.default_height / 2; |
break; |
case 'F': |
y = 0; |
h = this.default_height; |
break; |
} |
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); |
this.rectangles.add(rect); |
x += 2; |
} |
this.symbol_width = this.pattern[0].length() * 2; |
this.symbol_height = this.default_height; |
} |
} |
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/CodeOne.java |
---|
New file |
0,0 → 1,1869 |
/* |
* Copyright 2014 Robin Stuart |
* |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
* in compliance with the License. You may obtain a copy of the License at |
* |
* http://www.apache.org/licenses/LICENSE-2.0 |
* |
* Unless required by applicable law or agreed to in writing, software distributed under the License |
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
* or implied. See the License for the specific language governing permissions and limitations under |
* the License. |
*/ |
package uk.org.okapibarcode.backend; |
import java.math.BigInteger; |
import java.nio.charset.StandardCharsets; |
/** |
* <p> |
* Implements Code One. |
* |
* <p> |
* Code One is able to encode the ISO 8859-1 (Latin-1) character set or GS1 data. There are two |
* types of Code One symbol: variable height symbols which are roughly square (versions A thought to |
* H) and fixed-height versions (version S and T). Version S symbols can only encode numeric data. |
* The width of version S and version T symbols is determined by the length of the input data. |
* |
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a> |
*/ |
public class CodeOne extends Symbol { |
public enum Version { |
NONE, A, B, C, D, E, F, G, H, S, T |
} |
private static final int[] C40_SHIFT = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, |
0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; |
private static final int[] C40_VALUE = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22, |
23, 24, 25, 26, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }; |
private static final int[] TEXT_SHIFT = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, |
0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3 }; |
private static final int[] TEXT_VALUE = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, |
11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 22, 23, 24, 25, |
26, 0, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31 }; |
private static final int[] C1_HEIGHT = { 16, 22, 28, 40, 52, 70, 104, 148 }; |
private static final int[] C1_WIDTH = { 18, 22, 32, 42, 54, 76, 98, 134 }; |
private static final int[] C1_DATA_LENGTH = { 10, 19, 44, 91, 182, 370, 732, 1480 }; |
private static final int[] C1_ECC_LENGTH = { 10, 16, 26, 44, 70, 140, 280, 560 }; |
private static final int[] C1_BLOCKS = { 1, 1, 1, 1, 1, 2, 4, 8 }; |
private static final int[] C1_DATA_BLOCKS = { 10, 19, 44, 91, 182, 185, 183, 185 }; |
private static final int[] C1_ECC_BLOCKS = { 10, 16, 26, 44, 70, 70, 70, 70 }; |
private static final int[] C1_GRID_WIDTH = { 4, 5, 7, 9, 12, 17, 22, 30 }; |
private static final int[] C1_GRID_HEIGHT = { 5, 7, 10, 15, 21, 30, 46, 68 }; |
private enum Mode { |
C1_ASCII, C1_C40, C1_DECIMAL, C1_TEXT, C1_EDI, C1_BYTE |
} |
private Version preferredVersion = Version.NONE; |
private final int[] data = new int[1500]; |
private final int[][] datagrid = new int[136][120]; |
private final boolean[][] outputGrid = new boolean[148][134]; |
/** |
* Sets the preferred symbol size / version. Versions A to H are square symbols. Version S and T |
* are fixed height symbols. This value may be ignored if the input data does not fit in the |
* specified version. |
* |
* @param version the preferred symbol version |
*/ |
public void setPreferredVersion(final Version version) { |
this.preferredVersion = version; |
} |
/** |
* Returns the preferred symbol version. |
* |
* @return the preferred symbol version |
*/ |
public Version getPreferredVersion() { |
return this.preferredVersion; |
} |
@Override |
protected boolean gs1Supported() { |
return true; |
} |
@Override |
protected void encode() { |
int size = 1, i, j, data_blocks; |
int row, col; |
int sub_version = 0; |
int codewords; |
final int[] ecc = new int[600]; |
final int[] stream = new int[2100]; |
int block_width; |
final int length = this.content.length(); |
final ReedSolomon rs = new ReedSolomon(); |
int data_length; |
int data_cw, ecc_cw; |
final int[] sub_data = new int[190]; |
final StringBuilder bin = new StringBuilder(); |
if (!this.content.matches("[\u0000-\u00FF]+")) { |
throw new OkapiException("Invalid characters in input data"); |
} |
if (this.preferredVersion == Version.S) { |
/* Version S */ |
infoLine("Version: S"); |
if (length > 18) { |
throw new OkapiException("Input data too long"); |
} |
if (!this.content.matches("[0-9]+?")) { |
throw new OkapiException("Invalid characters in input"); |
} |
sub_version = 3; |
codewords = 12; |
block_width = 6; /* Version S-30 */ |
if (length <= 12) { |
sub_version = 2; |
codewords = 8; |
block_width = 4; |
} /* Version S-20 */ |
if (length <= 6) { |
sub_version = 1; |
codewords = 4; |
block_width = 2; |
} /* Version S-10 */ |
final BigInteger elreg = new BigInteger(this.content); |
for (i = 0; i < codewords; i++) { |
BigInteger codewordValue = elreg.shiftRight(5 * i); |
codewordValue = codewordValue.and(BigInteger.valueOf(0b11111)); |
this.data[codewords - i - 1] = codewordValue.intValue(); |
} |
logCodewords(codewords); |
rs.init_gf(0x25); |
rs.init_code(codewords, 1); |
rs.encode(codewords, this.data); |
infoLine("ECC Codeword Count: " + codewords); |
for (i = 0; i < codewords; i++) { |
stream[i] = this.data[i]; |
stream[i + codewords] = rs.getResult(codewords - i - 1); |
} |
for (i = 0; i < 136; i++) { |
for (j = 0; j < 120; j++) { |
this.datagrid[i][j] = '0'; |
} |
} |
i = 0; |
for (row = 0; row < 2; row++) { |
for (col = 0; col < block_width; col++) { |
if ((stream[i] & 0x10) != 0) { |
this.datagrid[row * 2][col * 5] = '1'; |
} |
if ((stream[i] & 0x08) != 0) { |
this.datagrid[row * 2][col * 5 + 1] = '1'; |
} |
if ((stream[i] & 0x04) != 0) { |
this.datagrid[row * 2][col * 5 + 2] = '1'; |
} |
if ((stream[i] & 0x02) != 0) { |
this.datagrid[row * 2 + 1][col * 5] = '1'; |
} |
if ((stream[i] & 0x01) != 0) { |
this.datagrid[row * 2 + 1][col * 5 + 1] = '1'; |
} |
if ((stream[i + 1] & 0x10) != 0) { |
this.datagrid[row * 2][col * 5 + 3] = '1'; |
} |
if ((stream[i + 1] & 0x08) != 0) { |
this.datagrid[row * 2][col * 5 + 4] = '1'; |
} |
if ((stream[i + 1] & 0x04) != 0) { |
this.datagrid[row * 2 + 1][col * 5 + 2] = '1'; |
} |
if ((stream[i + 1] & 0x02) != 0) { |
this.datagrid[row * 2 + 1][col * 5 + 3] = '1'; |
} |
if ((stream[i + 1] & 0x01) != 0) { |
this.datagrid[row * 2 + 1][col * 5 + 4] = '1'; |
} |
i += 2; |
} |
} |
infoLine("Grid Size: " + block_width + " X " + 2); |
size = 9; |
this.row_count = 8; |
this.symbol_width = 10 * sub_version + 1; |
} |
if (this.preferredVersion == Version.T) { |
/* Version T */ |
infoLine("Version: T"); |
for (i = 0; i < 40; i++) { |
this.data[i] = 0; |
} |
data_length = encodeAsCode1Data(); |
if (data_length > 38) { |
throw new OkapiException("Input data too long"); |
} |
size = 10; |
sub_version = 3; |
data_cw = 38; |
ecc_cw = 22; |
block_width = 12; |
if (data_length <= 24) { |
sub_version = 2; |
data_cw = 24; |
ecc_cw = 16; |
block_width = 8; |
} |
if (data_length <= 10) { |
sub_version = 1; |
data_cw = 10; |
ecc_cw = 10; |
block_width = 4; |
} |
logCodewords(data_length); |
for (i = data_length; i < data_cw; i++) { |
this.data[i] = 129; /* Pad */ |
} |
/* Calculate error correction data */ |
rs.init_gf(0x12d); |
rs.init_code(ecc_cw, 1); |
rs.encode(data_cw, this.data); |
infoLine("ECC Codeword Count: " + ecc_cw); |
/* "Stream" combines data and error correction data */ |
for (i = 0; i < data_cw; i++) { |
stream[i] = this.data[i]; |
} |
for (i = 0; i < ecc_cw; i++) { |
stream[data_cw + i] = rs.getResult(ecc_cw - i - 1); |
} |
for (i = 0; i < 136; i++) { |
for (j = 0; j < 120; j++) { |
this.datagrid[i][j] = '0'; |
} |
} |
i = 0; |
for (row = 0; row < 5; row++) { |
for (col = 0; col < block_width; col++) { |
if ((stream[i] & 0x80) != 0) { |
this.datagrid[row * 2][col * 4] = '1'; |
} |
if ((stream[i] & 0x40) != 0) { |
this.datagrid[row * 2][col * 4 + 1] = '1'; |
} |
if ((stream[i] & 0x20) != 0) { |
this.datagrid[row * 2][col * 4 + 2] = '1'; |
} |
if ((stream[i] & 0x10) != 0) { |
this.datagrid[row * 2][col * 4 + 3] = '1'; |
} |
if ((stream[i] & 0x08) != 0) { |
this.datagrid[row * 2 + 1][col * 4] = '1'; |
} |
if ((stream[i] & 0x04) != 0) { |
this.datagrid[row * 2 + 1][col * 4 + 1] = '1'; |
} |
if ((stream[i] & 0x02) != 0) { |
this.datagrid[row * 2 + 1][col * 4 + 2] = '1'; |
} |
if ((stream[i] & 0x01) != 0) { |
this.datagrid[row * 2 + 1][col * 4 + 3] = '1'; |
} |
i++; |
} |
} |
infoLine("Grid Size: " + block_width + " X " + 5); |
this.row_count = 16; |
this.symbol_width = sub_version * 16 + 1; |
} |
if (this.preferredVersion != Version.S && this.preferredVersion != Version.T) { |
/* Version A to H */ |
for (i = 0; i < 1500; i++) { |
this.data[i] = 0; |
} |
data_length = encodeAsCode1Data(); |
for (i = 7; i >= 0; i--) { |
if (C1_DATA_LENGTH[i] >= data_length) { |
size = i + 1; |
} |
} |
if (getSize(this.preferredVersion) > size) { |
size = getSize(this.preferredVersion); |
} |
final char version = (char) (size - 1 + 'A'); |
infoLine("Version: " + version); |
logCodewords(data_length); |
for (i = data_length; i < C1_DATA_LENGTH[size - 1]; i++) { |
this.data[i] = 129; /* Pad */ |
} |
/* Calculate error correction data */ |
data_length = C1_DATA_LENGTH[size - 1]; |
data_blocks = C1_BLOCKS[size - 1]; |
rs.init_gf(0x12d); |
rs.init_code(C1_ECC_BLOCKS[size - 1], 0); |
for (i = 0; i < data_blocks; i++) { |
for (j = 0; j < C1_DATA_BLOCKS[size - 1]; j++) { |
sub_data[j] = this.data[j * data_blocks + i]; |
} |
rs.encode(C1_DATA_BLOCKS[size - 1], sub_data); |
for (j = 0; j < C1_ECC_BLOCKS[size - 1]; j++) { |
ecc[C1_ECC_LENGTH[size - 1] - (j * data_blocks + i) - 1] = rs.getResult(j); |
} |
} |
infoLine("ECC Codeword Count: " + C1_ECC_LENGTH[size - 1]); |
/* "Stream" combines data and error correction data */ |
for (i = 0; i < data_length; i++) { |
stream[i] = this.data[i]; |
} |
for (i = 0; i < C1_ECC_LENGTH[size - 1]; i++) { |
stream[data_length + i] = ecc[i]; |
} |
for (i = 0; i < 136; i++) { |
for (j = 0; j < 120; j++) { |
this.datagrid[i][j] = '0'; |
} |
} |
i = 0; |
for (row = 0; row < C1_GRID_HEIGHT[size - 1]; row++) { |
for (col = 0; col < C1_GRID_WIDTH[size - 1]; col++) { |
if ((stream[i] & 0x80) != 0) { |
this.datagrid[row * 2][col * 4] = '1'; |
} |
if ((stream[i] & 0x40) != 0) { |
this.datagrid[row * 2][col * 4 + 1] = '1'; |
} |
if ((stream[i] & 0x20) != 0) { |
this.datagrid[row * 2][col * 4 + 2] = '1'; |
} |
if ((stream[i] & 0x10) != 0) { |
this.datagrid[row * 2][col * 4 + 3] = '1'; |
} |
if ((stream[i] & 0x08) != 0) { |
this.datagrid[row * 2 + 1][col * 4] = '1'; |
} |
if ((stream[i] & 0x04) != 0) { |
this.datagrid[row * 2 + 1][col * 4 + 1] = '1'; |
} |
if ((stream[i] & 0x02) != 0) { |
this.datagrid[row * 2 + 1][col * 4 + 2] = '1'; |
} |
if ((stream[i] & 0x01) != 0) { |
this.datagrid[row * 2 + 1][col * 4 + 3] = '1'; |
} |
i++; |
} |
} |
infoLine("Grid Size: " + C1_GRID_WIDTH[size - 1] + " X " + C1_GRID_HEIGHT[size - 1]); |
this.row_count = C1_HEIGHT[size - 1]; |
this.symbol_width = C1_WIDTH[size - 1]; |
} |
for (i = 0; i < 148; i++) { |
for (j = 0; j < 134; j++) { |
this.outputGrid[i][j] = false; |
} |
} |
switch (size) { |
case 1: |
/* Version A */ |
plotCentralFinder(6, 3, 1); |
plotVerticalBar(4, 6, 1); |
plotVerticalBar(12, 5, 0); |
setGridModule(5, 12); |
plotSpigot(0); |
plotSpigot(15); |
plotDataBlock(0, 0, 5, 4, 0, 0); |
plotDataBlock(0, 4, 5, 12, 0, 2); |
plotDataBlock(5, 0, 5, 12, 6, 0); |
plotDataBlock(5, 12, 5, 4, 6, 2); |
break; |
case 2: |
/* Version B */ |
plotCentralFinder(8, 4, 1); |
plotVerticalBar(4, 8, 1); |
plotVerticalBar(16, 7, 0); |
setGridModule(7, 16); |
plotSpigot(0); |
plotSpigot(21); |
plotDataBlock(0, 0, 7, 4, 0, 0); |
plotDataBlock(0, 4, 7, 16, 0, 2); |
plotDataBlock(7, 0, 7, 16, 8, 0); |
plotDataBlock(7, 16, 7, 4, 8, 2); |
break; |
case 3: |
/* Version C */ |
plotCentralFinder(11, 4, 2); |
plotVerticalBar(4, 11, 1); |
plotVerticalBar(26, 13, 1); |
plotVerticalBar(4, 10, 0); |
plotVerticalBar(26, 10, 0); |
plotSpigot(0); |
plotSpigot(27); |
plotDataBlock(0, 0, 10, 4, 0, 0); |
plotDataBlock(0, 4, 10, 20, 0, 2); |
plotDataBlock(0, 24, 10, 4, 0, 4); |
plotDataBlock(10, 0, 10, 4, 8, 0); |
plotDataBlock(10, 4, 10, 20, 8, 2); |
plotDataBlock(10, 24, 10, 4, 8, 4); |
break; |
case 4: |
/* Version D */ |
plotCentralFinder(16, 5, 1); |
plotVerticalBar(4, 16, 1); |
plotVerticalBar(20, 16, 1); |
plotVerticalBar(36, 16, 1); |
plotVerticalBar(4, 15, 0); |
plotVerticalBar(20, 15, 0); |
plotVerticalBar(36, 15, 0); |
plotSpigot(0); |
plotSpigot(12); |
plotSpigot(27); |
plotSpigot(39); |
plotDataBlock(0, 0, 15, 4, 0, 0); |
plotDataBlock(0, 4, 15, 14, 0, 2); |
plotDataBlock(0, 18, 15, 14, 0, 4); |
plotDataBlock(0, 32, 15, 4, 0, 6); |
plotDataBlock(15, 0, 15, 4, 10, 0); |
plotDataBlock(15, 4, 15, 14, 10, 2); |
plotDataBlock(15, 18, 15, 14, 10, 4); |
plotDataBlock(15, 32, 15, 4, 10, 6); |
break; |
case 5: |
/* Version E */ |
plotCentralFinder(22, 5, 2); |
plotVerticalBar(4, 22, 1); |
plotVerticalBar(26, 24, 1); |
plotVerticalBar(48, 22, 1); |
plotVerticalBar(4, 21, 0); |
plotVerticalBar(26, 21, 0); |
plotVerticalBar(48, 21, 0); |
plotSpigot(0); |
plotSpigot(12); |
plotSpigot(39); |
plotSpigot(51); |
plotDataBlock(0, 0, 21, 4, 0, 0); |
plotDataBlock(0, 4, 21, 20, 0, 2); |
plotDataBlock(0, 24, 21, 20, 0, 4); |
plotDataBlock(0, 44, 21, 4, 0, 6); |
plotDataBlock(21, 0, 21, 4, 10, 0); |
plotDataBlock(21, 4, 21, 20, 10, 2); |
plotDataBlock(21, 24, 21, 20, 10, 4); |
plotDataBlock(21, 44, 21, 4, 10, 6); |
break; |
case 6: |
/* Version F */ |
plotCentralFinder(31, 5, 3); |
plotVerticalBar(4, 31, 1); |
plotVerticalBar(26, 35, 1); |
plotVerticalBar(48, 31, 1); |
plotVerticalBar(70, 35, 1); |
plotVerticalBar(4, 30, 0); |
plotVerticalBar(26, 30, 0); |
plotVerticalBar(48, 30, 0); |
plotVerticalBar(70, 30, 0); |
plotSpigot(0); |
plotSpigot(12); |
plotSpigot(24); |
plotSpigot(45); |
plotSpigot(57); |
plotSpigot(69); |
plotDataBlock(0, 0, 30, 4, 0, 0); |
plotDataBlock(0, 4, 30, 20, 0, 2); |