OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Compare Revisions

Regard whitespace Rev 180 → Rev 181

/trunk/Modules/Module 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/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/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 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/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/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/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/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 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/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 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 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/.settings/org.eclipse.jdt.core.prefs
New file
0,0 → 1,7
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8
/trunk/Modules/Module Label/.classpath
1,7 → 1,7
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry combineaccessrules="false" kind="src" path="/OpenConcerto"/>
<classpathentry kind="output" path="bin"/>
</classpath>
/trunk/Modules/Module Label/lib/barcode4j-2.1.0.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/lib/barcode4j-2.1.0.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/lib/jbarcode-0.2.8.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/lib/jbarcode-0.2.8.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/default-zpl.txt
New file
0,0 → 1,20
^XA
 
^FX Titre
^CF0,42^FO150,43^FB750,1,0,L^FDXXXXXXXXXX^FS
^FX DataMatrix
^FO1010,20^BXN,7,200,,,,@^FDOPENCONCERTO^FS
 
^FX Nom du produit
^CF0,40
^FO150,140^FB700,1,0,L^FDYYYYYYYYYY^FS
 
^FX Code barre
^FO150,220
^BY3,2,300
^BCN,300,Y,N,Y,D
^FD(02)7612345678900(15)201218(37)9999^FS
 
^FO150,620^BY3,2,300^BCN,300,Y,N,Y,D^FD(10)LLLLLLLLL^FS
 
^XZ
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/ModuleLabel.java
4,10 → 4,17
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;
 
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.SwingWorker;
 
import org.openconcerto.erp.generationDoc.provider.AdresseFullClientValueProvider;
14,6 → 21,7
import org.openconcerto.erp.modules.AbstractModule;
import org.openconcerto.erp.modules.ComponentsContext;
import org.openconcerto.erp.modules.ModuleFactory;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
23,10 → 31,12
import org.openconcerto.sql.view.list.IListeAction.IListeEvent;
import org.openconcerto.sql.view.list.RowAction.PredicateRowAction;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.GestionDevise;
import org.openconcerto.utils.StringUtils;
 
public final class ModuleLabel extends AbstractModule {
final LinkedHashMap<String, String> zplTemplates = new LinkedHashMap<String, String>();
 
public ModuleLabel(ModuleFactory f) throws IOException {
super(f);
34,6 → 44,9
 
@Override
protected void setupComponents(ComponentsContext ctxt) {
readTemplates(new File("Template/Labels"));
readTemplates(new File("Configuration/Template/Labels"));
 
final String actionName = "Imprimer les étiquettes";
final PredicateRowAction aArticle = new PredicateRowAction(new AbstractAction(actionName) {
 
41,26 → 54,31
public void actionPerformed(ActionEvent arg0) {
final IListe list = IListe.get(arg0);
final List<Integer> selectedIDs = list.getSelection().getSelectedIDs();
final SwingWorker<List<SQLRowValues>, String> wworker = new SwingWorker<List<SQLRowValues>, String>() {
final SQLTable tArticle = list.getSelectedRows().get(0).getTable();
final SwingWorker<List<RowValuesLabel>, String> wworker = new SwingWorker<List<RowValuesLabel>, String>() {
 
@Override
protected List<SQLRowValues> doInBackground() throws Exception {
final SQLTable tArticle = list.getSelectedRows().get(0).getTable();
protected List<RowValuesLabel> doInBackground() throws Exception {
final SQLRowValues graph = new SQLRowValues(tArticle);
graph.putNulls("NOM", "PV_TTC");
final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(graph);
final List<SQLRowValues> values = fetcher.fetch(new Where(tArticle.getKey(), selectedIDs));
return values;
final List<SQLRowValues> rows = fetcher.fetch(new Where(tArticle.getKey(), selectedIDs));
final List<RowValuesLabel> list = new ArrayList<>(rows.size());
for (SQLRowValues row : rows) {
list.add(new RowValuesLabel(row));
}
return list;
}
 
@Override
protected void done() {
try {
final List<SQLRowValues> values = get();
final List<RowValuesLabel> values = get();
 
final LabelFrame f = new LabelFrame(values, new LabelRenderer() {
 
@Override
public void paintLabel(Graphics g, SQLRowAccessor row, int x, int y, int gridWith, int gridHeight, float fontSize) {
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize) {
g.setColor(Color.BLACK);
g.setFont(g.getFont().deriveFont(fontSize));
// Labels borders
67,6 → 85,7
final int hBorder = 12;
final int vBorder = 8;
// Product name
SQLRowValues row = ((RowValuesLabel) label).getSQLRowValues();
final String text = row.getString("NOM");
final List<String> l = StringUtils.wrap(text, g.getFontMetrics(), gridWith - 2 * hBorder);
final int lineHeight = g.getFontMetrics().getHeight();
73,7 → 92,7
int lineY = y;
final int margin = gridHeight - l.size() * lineHeight;
if (margin > 0) {
lineY += (int) (margin / 2);
lineY += margin / 2;
}
for (String line : l) {
g.drawString(line, x + hBorder, lineY);
95,8 → 114,8
} catch (Exception e) {
ExceptionHandler.handle("Erreur d'impression", e);
}
}
};
};
wworker.execute();
 
}
107,11 → 126,11
public void actionPerformed(ActionEvent arg0) {
final IListe list = IListe.get(arg0);
final List<Integer> selectedIDs = list.getSelection().getSelectedIDs();
final SwingWorker<List<SQLRowValues>, String> wworker = new SwingWorker<List<SQLRowValues>, String>() {
final SQLTable tClient = list.getSelectedRows().get(0).getTable();
final SwingWorker<List<RowValuesLabel>, String> wworker = new SwingWorker<List<RowValuesLabel>, String>() {
 
@Override
protected List<SQLRowValues> doInBackground() throws Exception {
final SQLTable tClient = list.getSelectedRows().get(0).getTable();
protected List<RowValuesLabel> doInBackground() throws Exception {
final SQLRowValues graph = new SQLRowValues(tClient);
graph.putNulls("NOM");
final SQLRowValues a1 = graph.putRowValues("ID_ADRESSE");
119,17 → 138,24
final SQLRowValues a2 = graph.putRowValues("ID_ADRESSE_L");
a2.putNulls(a2.getTable().getFieldsName());
final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(graph);
final List<SQLRowValues> values = fetcher.fetch(new Where(tClient.getKey(), selectedIDs));
return values;
final List<SQLRowValues> rows = fetcher.fetch(new Where(tClient.getKey(), selectedIDs));
 
final List<RowValuesLabel> list = new ArrayList<>(rows.size());
for (SQLRowValues row : rows) {
list.add(new RowValuesLabel(row));
}
return list;
}
 
@Override
protected void done() {
try {
final List<SQLRowValues> values = get();
final List<RowValuesLabel> values = get();
final LabelFrame f = new LabelFrame(values, new LabelRenderer() {
 
@Override
public void paintLabel(Graphics g, SQLRowAccessor row, int x, int y, int gridWith, int gridHeight, float fontSize) {
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize) {
SQLRowValues row = ((RowValuesLabel) label).getSQLRowValues();
SQLRowAccessor rAddr = row.getForeign("ID_ADRESSE_L");
if (rAddr == null || rAddr.isUndefined()) {
rAddr = row.getForeign("ID_ADRESSE");
165,8 → 191,8
} catch (Exception e) {
ExceptionHandler.handle("Erreur d'impression", e);
}
}
};
};
wworker.execute();
 
}
176,13 → 202,94
aClient.setPredicate(IListeEvent.createSelectionCountPredicate(1, Integer.MAX_VALUE));
ctxt.getElement("ARTICLE").getRowActions().add(aArticle);
ctxt.getElement("CLIENT").getRowActions().add(aClient);
 
if (!this.zplTemplates.isEmpty()) {
for (final Entry<String, String> entry : this.zplTemplates.entrySet()) {
final String zpl = entry.getValue();
final PredicateRowAction action = new PredicateRowAction(new AbstractAction("Imprimer l'étiquette " + entry.getKey()) {
 
@Override
public void actionPerformed(ActionEvent arg0) {
final ZPLPrinterPanel p = new ZPLPrinterPanel(zpl);
final JFrame f = new JFrame();
final IListe list = IListe.get(arg0);
final int idProduct = list.getSelection().getSelectedID();
final SQLTable tArticle = list.getSelectedRows().get(0).getTable();
 
final SwingWorker<SQLRowValues, String> wworker = new SwingWorker<SQLRowValues, String>() {
 
@Override
protected SQLRowValues doInBackground() throws Exception {
final SQLRow row = tArticle.getRow(idProduct);
row.fetchValues();
return row.asRowValues();
}
 
@Override
protected void done() {
try {
final SQLRowValues values = get();
p.initUI(values);
f.setTitle(entry.getKey());
f.setContentPane(p);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
 
} catch (Exception e) {
ExceptionHandler.handle("Erreur d'impression", e);
}
}
};
wworker.execute();
 
}
}, true, false);
 
action.setPredicate(IListeEvent.createSelectionCountPredicate(1, 1));
ctxt.getElement("ARTICLE").getRowActions().add(action);
}
}
 
}
 
@Override
protected void start() {
 
}
 
private void readTemplates(File templatesDir) {
System.out.println("ModuleLabel.readTemplates() " + templatesDir.getAbsolutePath());
if (templatesDir.exists() && templatesDir.isDirectory()) {
System.err.println("ModuleLabel.readTemplates() " + templatesDir.getAbsolutePath());
File[] files = templatesDir.listFiles();
if (files != null) {
LinkedHashMap<String, String> map = new LinkedHashMap<>();
for (File f : files) {
if (f.getName().endsWith(".zpl")) {
try {
String zpl = FileUtils.read(f, StandardCharsets.UTF_8);
String name = f.getName().substring(0, f.getName().length() - 4).trim();
map.put(name, zpl);
System.err.println("ModuleLabel.readTemplates() add " + name);
} catch (Exception e) {
System.err.println(this.getClass().getCanonicalName() + "start() cannot read zpl template : " + f.getAbsolutePath() + " : " + e.getMessage());
}
}
}
// Tri de la map par clef
final TreeMap<String, String> copy = new TreeMap<>(map);
this.zplTemplates.clear();
this.zplTemplates.putAll(copy);
}
 
} else {
System.err.println("ModuleLabel.readTemplates() " + templatesDir.getAbsolutePath() + " missing");
}
}
 
@Override
protected void stop() {
// nothing
}
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/32.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/32.png
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/RowValuesLabel.java
New file
0,0 → 1,16
package org.openconcerto.modules.label;
 
import org.openconcerto.sql.model.SQLRowValues;
 
public class RowValuesLabel extends Label {
private SQLRowValues row;
 
public RowValuesLabel(SQLRowValues row) {
this.row = row;
}
 
public SQLRowValues getSQLRowValues() {
return row;
}
 
}
/trunk/Modules/Module Label/src/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()));
// Print
 
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/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/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/GS1Frame.java
New file
0,0 → 1,1049
package org.openconcerto.modules.label;
 
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
 
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;
import javax.print.SimpleDoc;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.PlainDocument;
 
import org.openconcerto.modules.label.filter.BatchDocumentFilter;
import org.openconcerto.modules.label.filter.EANDocumentFilter;
import org.openconcerto.modules.label.filter.LimitDocumentFilter;
import org.openconcerto.modules.label.filter.NumberOfProductDocumentFilter;
import org.openconcerto.modules.label.gs1.GS1AIElements;
import org.openconcerto.modules.label.gs1.GS1Util;
import org.openconcerto.ui.JDate;
 
import uk.org.okapibarcode.backend.Code128;
import uk.org.okapibarcode.backend.DataMatrix;
import uk.org.okapibarcode.backend.DataMatrix.ForceMode;
import uk.org.okapibarcode.backend.Ean;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.output.Java2DRenderer;
 
public class GS1Frame extends JFrame {
 
private final SimpleDateFormat dfDate = new SimpleDateFormat("yyMMdd");
private final JTextField textSupplier = new JTextField(20);
private final JTextField textName = new JTextField(20);
private final JTextField textEAN = new JTextField(14);
private final JTextField textBatch = new JTextField(20);
private final JSpinner portZPL = new JSpinner(new SpinnerNumberModel(9100, 24, 10000, 1));
private final JLabel labelEan = new JLabel("EAN 13/14", SwingConstants.RIGHT);
private final JTextField textNumberOfProducts = new JTextField(12);
private final JSpinner labelsSpinner = new JSpinner(new SpinnerNumberModel(5, 0, 9000, 1));
private final JTextField labelBarCode = new JTextField(64);
private final JDate dateDLUODLC = new JDate();
private final JRadioButton b1 = new JRadioButton("DDM/DLUO");
private final JCheckBox checkIgnoreMargins = new JCheckBox("Ignorer les marges de l'imprimante");
private final JRadioButton twoPerPage = new JRadioButton("2");
private final JRadioButton fourPerPage = new JRadioButton("4");
private final JRadioButton radioZPLUSB = new JRadioButton("imprimante locale");
private final JTextField textIP = new JTextField(20);
private final JTabbedPane tabs = new JTabbedPane();
private LabelPanel labelPanel;
private BufferedImage barcodeImage = null;
private BufferedImage barcodeImageBatch = null;
private BufferedImage barcodeDatamatrix = null;
private final Properties properties = new Properties();
 
public GS1Frame() {
super("Code à barres GS1-128");
 
final File file = new File("openconcerto-gs1.properties");
if (file.exists()) {
try {
properties.load(new FileInputStream(file));
} catch (IOException e) {
e.printStackTrace();
}
}
 
final JSplitPane split = new JSplitPane();
final JPanel left = createLeftContent();
final JComponent right = createRightContent();
split.setLeftComponent(left);
split.setRightComponent(right);
this.setContentPane(split);
split.setEnabled(false);
try {
updateList();
} catch (Exception e) {
e.printStackTrace();
}
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
try {
saveProperties();
} catch (IOException e1) {
e1.printStackTrace();
}
dispose();
}
});
}
 
public void saveProperties() throws FileNotFoundException, IOException {
properties.setProperty("supplier", this.textSupplier.getText());
properties.setProperty("product", this.textName.getText());
properties.setProperty("ean", this.textEAN.getText());
properties.setProperty("nbOfProdcuts", this.textNumberOfProducts.getText());
properties.setProperty("batch", this.textBatch.getText());
final Date value = this.dateDLUODLC.getValue();
if (value != null) {
properties.setProperty("sellByDate", String.valueOf(value.getTime()));
} else {
properties.setProperty("sellByDate", "");
}
properties.setProperty("sellBy", b1.isSelected() ? "true" : "false");
properties.setProperty("nbLabels", this.labelsSpinner.getValue().toString());
properties.setProperty("labelsPerPage", fourPerPage.isSelected() ? "4" : "2");
properties.setProperty("ignoreMargin", checkIgnoreMargins.isSelected() ? "true" : "false");
properties.setProperty("usbPrinter", this.radioZPLUSB.isSelected() ? "true" : "false");
properties.setProperty("zplIP", this.textIP.getText());
properties.setProperty("zplPort", this.portZPL.getValue().toString());
properties.setProperty("tab", String.valueOf(tabs.getSelectedIndex()));
 
final FileOutputStream out = new FileOutputStream("openconcerto-gs1.properties");
properties.store(out, "");
out.flush();
out.close();
}
 
boolean isEANValid() {
final String ean = this.textEAN.getText().trim();
if (ean.length() < 13) {
return false;
}
final char cd1 = Ean.calcDigit(ean.substring(0, ean.length() - 1));
final char cd2 = ean.charAt(ean.length() - 1);
return (cd1 == cd2);
}
 
private void updateList() throws Exception {
this.barcodeImage = null;
this.barcodeImageBatch = null;
this.barcodeDatamatrix = null;
this.labelPanel.setList(new ArrayList<>());
this.labelBarCode.setText("");
// Check EAN
String ean = this.textEAN.getText().trim();
if (!isEANValid()) {
this.labelEan.setForeground(Color.RED);
} else {
this.labelEan.setForeground(Color.BLACK);
}
if (ean.length() == 13) {
this.labelEan.setText("EAN 13");
} else if (ean.length() == 14) {
this.labelEan.setText("EAN 14");
} else {
this.labelEan.setText("EAN invalide");
this.labelEan.setForeground(Color.RED);
}
 
if (!isEANValid()) {
return;
}
 
int n = ((Number) this.labelsSpinner.getValue()).intValue();
this.labelBarCode.setText(this.getGS1().formatHumanReadable());
this.labelBarCode.setEditable(false);
GS1Label l = new GS1Label(this.textSupplier.getText(), this.textName.getText(), this.getGS1());
List<GS1Label> list = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
list.add(l);
}
this.labelPanel.setList(list);
 
// Configure the barcode generator
// adjust barcode width here
// 32mm de hatu minimum
// 0,495mm et 1,016mm pour largeur de barre
 
GS1Util util = new GS1Util();
 
{
final GS1AIElements gs128 = getGS128();
Code128 dataMatrix = new Code128();
dataMatrix.setDataType(Symbol.DataType.GS1);
dataMatrix.setBarHeight(70);
dataMatrix.setContent(util.formatDataMatrix(gs128));
int magnification = 5;
int borderSize = 3;
this.barcodeImage = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = this.barcodeImage.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize));
Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK);
renderer.render(dataMatrix);
}
final GS1AIElements gs128b = getGS128Batch();
if (!gs128b.isEmpty()) {
Code128 dataMatrix = new Code128();
dataMatrix.setDataType(Symbol.DataType.GS1);
dataMatrix.setBarHeight(90);
dataMatrix.setContent(util.formatDataMatrix(gs128b));
int magnification = 4;
int borderSize = 3;
this.barcodeImageBatch = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize), (int) (dataMatrix.getHeight() * magnification) + (2 * borderSize),
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = this.barcodeImageBatch.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize));
Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK);
renderer.render(dataMatrix);
}
 
{
String s = util.formatDataMatrix(getGS1());
DataMatrix dataMatrix = new DataMatrix();
dataMatrix.setDataType(Symbol.DataType.GS1);
dataMatrix.setForceMode(ForceMode.SQUARE);
dataMatrix.setContent(s);
int magnification = 30;
int borderSize = 3;
this.barcodeDatamatrix = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize),
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = this.barcodeDatamatrix.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize), (dataMatrix.getHeight() * magnification) + (2 * borderSize));
Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK);
renderer.render(dataMatrix);
}
 
// ImageIO.write(barcodeImage, "PNG", new File("barcode.png"));
// ImageIO.write(barcodeImageBatch, "PNG", new File("barcode2.png"));
// ImageIO.write(barcodeDatamatrix, "PNG", new File("datamatrix.png"));
 
}
 
private JPanel createLeftContent() {
final JPanel p = new JPanel();
p.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.gridy = 0;
c.gridx = 0;
c.gridwidth = 3;
p.add(createLogo(), c);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridwidth = 1;
c.gridy++;
c.anchor = GridBagConstraints.WEST;
c.insets = new Insets(2, 3, 2, 2);
//
c.weightx = 0;
c.gridwidth = 1;
p.add(new JLabel("Fournisseur / fabricant", SwingConstants.RIGHT), c);
c.gridx++;
c.weightx = 1;
c.gridwidth = 2;
this.textSupplier.setText(properties.getProperty("supplier", ""));
((AbstractDocument) this.textSupplier.getDocument()).setDocumentFilter(new LimitDocumentFilter(22));
p.add(this.textSupplier, c);
 
//
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.gridwidth = 1;
p.add(new JLabel("Désignation du produit", SwingConstants.RIGHT), c);
c.gridx++;
c.weightx = 1;
c.gridwidth = 2;
this.textName.setText(properties.getProperty("product", "Biscuit Extra"));
((AbstractDocument) this.textName.getDocument()).setDocumentFilter(new LimitDocumentFilter(22));
p.add(this.textName, c);
 
// EAN 13/14
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.gridwidth = 1;
p.add(this.labelEan, c);
c.gridx++;
c.weightx = 1;
c.gridwidth = 2;
this.textEAN.setText(properties.getProperty("ean", "7612345678900"));
((AbstractDocument) this.textEAN.getDocument()).setDocumentFilter(new EANDocumentFilter());
p.add(this.textEAN, c);
// nb de produit
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.gridwidth = 1;
p.add(new JLabel("Nombre par carton", SwingConstants.RIGHT), c);
c.gridx++;
c.weightx = 1;
c.gridwidth = 2;
c.fill = GridBagConstraints.NONE;
PlainDocument doc = (PlainDocument) this.textNumberOfProducts.getDocument();
doc.setDocumentFilter(new NumberOfProductDocumentFilter());
this.textNumberOfProducts.setText(properties.getProperty("nbOfProdcuts", "10"));
p.add(this.textNumberOfProducts, c);
// Numéro de lot
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.gridwidth = 1;
c.fill = GridBagConstraints.HORIZONTAL;
p.add(new JLabel("Lot", SwingConstants.RIGHT), c);
c.gridx++;
c.weightx = 1;
c.gridwidth = 2;
((AbstractDocument) this.textBatch.getDocument()).setDocumentFilter(new BatchDocumentFilter());
this.textBatch.setText(properties.getProperty("batch", ""));
p.add(this.textBatch, c);
// DLC
c.gridx = 1;
c.gridy++;
c.weightx = 0;
c.gridwidth = 1;
 
p.add(this.b1, c);
c.gridx++;
c.weightx = 1;
this.dateDLUODLC.setFormat(new SimpleDateFormat("dd/MM/yyyy"));
if (!properties.getProperty("sellByDate", "").isEmpty()) {
this.dateDLUODLC.setDateInMillis(Long.parseLong(properties.getProperty("sellByDate")));
}
p.add(this.dateDLUODLC, c);
// DLUO
c.gridx = 1;
c.gridy++;
c.weightx = 0;
JRadioButton b2 = new JRadioButton("DLC");
p.add(b2, c);
c.gridx++;
c.weightx = 1;
 
this.b1.setSelected(properties.getProperty("sellBy", "true").equals("true"));
b2.setSelected(!properties.getProperty("sellBy", "true").equals("true"));
// Barcode
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.gridwidth = 3;
p.add(new JLabel("Code GS1 complet de l'étiquette", SwingConstants.LEFT), c);
c.gridy++;
c.weightx = 1;
this.labelBarCode.setFont(this.textBatch.getFont());
this.labelBarCode.setEnabled(false);
p.add(this.labelBarCode, c);
 
// Nombre d'étiquettes
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.gridwidth = 1;
p.add(new JLabel("Nombre d'étiquettes", SwingConstants.RIGHT), c);
c.gridx++;
c.weightx = 0;
c.fill = GridBagConstraints.NONE;
 
this.labelsSpinner.setValue(Integer.parseInt(properties.getProperty("nbLabels", "4")));
 
p.add(this.labelsSpinner, c);
 
final JPanel spacer = new JPanel();
spacer.setOpaque(false);
c.gridy++;
c.weighty = 1;
p.add(spacer, c);
 
final ButtonGroup gr = new ButtonGroup();
gr.add(this.b1);
gr.add(b2);
 
// Listeners
 
final ChangeListener changeListener = new ChangeListener() {
 
@Override
public void stateChanged(ChangeEvent e) {
try {
updateList();
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
this.labelsSpinner.addChangeListener(changeListener);
 
final DocumentListener dListener = new DocumentListener() {
 
@Override
public void removeUpdate(DocumentEvent e) {
changedUpdate(e);
}
 
@Override
public void insertUpdate(DocumentEvent e) {
changedUpdate(e);
 
}
 
@Override
public void changedUpdate(DocumentEvent e) {
try {
updateList();
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
this.textSupplier.getDocument().addDocumentListener(dListener);
this.textName.getDocument().addDocumentListener(dListener);
this.textEAN.getDocument().addDocumentListener(dListener);
this.textNumberOfProducts.getDocument().addDocumentListener(dListener);
this.textBatch.getDocument().addDocumentListener(dListener);
 
this.dateDLUODLC.addValueListener(new PropertyChangeListener() {
 
@Override
public void propertyChange(PropertyChangeEvent evt) {
try {
updateList();
} catch (Exception e) {
e.printStackTrace();
}
 
}
});
this.b1.addChangeListener(changeListener);
b2.addChangeListener(changeListener);
return p;
}
 
private Component createLogo() {
final Image i1 = new ImageIcon(this.getClass().getResource("logo.png")).getImage();
final Image i2 = new ImageIcon(this.getClass().getResource("oc-qrcode.png")).getImage();
return new JComponent() {
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(i1, 0, 3, null);
g.drawImage(i2, getWidth() - i2.getWidth(null), 0, null);
g.setColor(Color.GRAY);
g.drawLine(0, 58, getWidth(), 58);
}
 
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 59);
}
};
}
 
private JComponent createRightContent() {
 
final JPanel panelA4 = new JPanel();
panelA4.setOpaque(false);
panelA4.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(2, 3, 2, 2);
c.gridx = 0;
c.gridy = 0;
panelA4.add(createToolBar(), c);
c.gridy++;
// panelA4.add(createToolBar2(), c);
// c.gridy++;
c.insets = new Insets(0, 0, 0, 0);
panelA4.add(new JSeparator(JSeparator.HORIZONTAL), c);
c.gridy++;
c.weightx = 1;
c.weighty = 1;
c.fill = GridBagConstraints.BOTH;
final int columnCount = fourPerPage.isSelected() ? 2 : 1;
this.labelPanel = new LabelPanel(new ArrayList<Label>(), 2, columnCount, createRenderer()) {
@Override
public Dimension getPreferredSize() {
return new Dimension(190 * 3, 280 * 3);
}
};
this.labelPanel.setIgnoreMargins(checkIgnoreMargins.isSelected());
panelA4.add(this.labelPanel, c);
tabs.addTab("Feuilles A4", panelA4);
tabs.addTab("Imprimante ZPL", createZPLPanel());
tabs.setSelectedIndex(Integer.parseInt(properties.getProperty("tab", "0")));
return tabs;
}
 
private Component createZPLPanel() {
final JPanel panelZPL = new JPanel();
panelZPL.setOpaque(false);
panelZPL.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(2, 3, 2, 2);
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
c.gridwidth = 1;
c.anchor = GridBagConstraints.EAST;
 
radioZPLUSB.setOpaque(false);
panelZPL.add(radioZPLUSB, c);
c.gridx += 5;
c.weightx = 1;
c.fill = GridBagConstraints.NONE;
c.insets = new Insets(4, 3, 2, 4);
final JButton buttonPrintZPL = new JButton("Imprimer");
buttonPrintZPL.setOpaque(false);
panelZPL.add(buttonPrintZPL, c);
c.insets = new Insets(2, 3, 2, 2);
c.gridx = 0;
c.gridy++;
c.weightx = 0;
c.fill = GridBagConstraints.HORIZONTAL;
final JRadioButton radioZPLNetwork = new JRadioButton("imprimante réseau");
radioZPLNetwork.setOpaque(false);
panelZPL.add(radioZPLNetwork, c);
c.gridx++;
panelZPL.add(new JLabel("Adresse"), c);
c.gridx++;
 
textIP.setText(properties.getProperty("zplIP", "192.168.1.50"));
panelZPL.add(textIP, c);
c.gridx++;
panelZPL.add(new JLabel("Port"), c);
this.portZPL.setValue(Integer.parseInt(properties.getProperty("zplPort", "9100")));
c.gridx++;
panelZPL.add(this.portZPL, c);
c.gridx = 0;
c.gridy++;
c.gridwidth = 6;
 
radioZPLUSB.setSelected(properties.getProperty("usbPrinter", "true").equals("true"));
radioZPLNetwork.setSelected(!properties.getProperty("usbPrinter", "true").equals("true"));
 
final ButtonGroup g = new ButtonGroup();
g.add(radioZPLUSB);
g.add(radioZPLNetwork);
 
c.anchor = GridBagConstraints.NORTHWEST;
panelZPL.add(new JLabel("L'impression nécessite une imprimante d'étiquettes compatible ZPL (Zebra ou autre)."), c);
c.gridy++;
c.weighty = 1;
panelZPL.add(new JLabel("Vous pouvez paramétrer l'impression par défaut (largeur 6\" à 203 dpi) dans le fichier zpl.txt ."), c);
buttonPrintZPL.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
if (!isEANValid()) {
JOptionPane.showMessageDialog(GS1Frame.this, "EAN invalide");
return;
}
 
final String code = createZPLCode();
System.out.println("ZPL:");
System.out.println(code);
byte[] data = code.getBytes(StandardCharsets.US_ASCII);
if (radioZPLNetwork.isSelected()) {
Socket socket = null;
try {
socket = new Socket(textIP.getText(), ((Number) GS1Frame.this.portZPL.getValue()).intValue());
final DataOutputStream out = new DataOutputStream(socket.getOutputStream());
final int nb = ((Number) labelsSpinner.getValue()).intValue();
for (int i = 0; i < nb; i++) {
out.write(data);
}
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(buttonPrintZPL, "Erreur d'impression réseau : " + ex.getMessage());
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
 
}
} else {
try {
final PrinterJob pj1 = PrinterJob.getPrinterJob();
if (pj1.printDialog()) {
final PrintService ps = pj1.getPrintService();
final DocPrintJob pj = ps.createPrintJob();
final SimpleDoc doc = new SimpleDoc(data, DocFlavor.BYTE_ARRAY.AUTOSENSE, null);
final int nb = ((Number) labelsSpinner.getValue()).intValue();
for (int i = 0; i < nb; i++) {
pj.print(doc, null);
}
}
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(buttonPrintZPL, "Erreur d'impression locale : " + ex.getMessage());
}
}
}
 
});
 
return panelZPL;
 
}
 
private LabelRenderer createRenderer() {
return new LabelRenderer() {
 
@Override
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize) {
final Graphics2D g2d = (Graphics2D) g;
g.setColor(Color.BLACK);
g2d.setStroke(new BasicStroke(3));
String str1 = GS1Frame.this.textSupplier.getText().trim();
String str2 = GS1Frame.this.textName.getText();
int pos1 = 26;
if (str1.isEmpty()) {
str1 = GS1Frame.this.textName.getText();
str2 = "";
pos1 += 16;
}
 
final AffineTransform defaultAt = g2d.getTransform();
final AffineTransform at = ((AffineTransform) (g2d.getTransform().clone()));
at.rotate(-Math.PI / 2);
g2d.setTransform(at);
g.setFont(getFont().deriveFont(15f));
final int d1 = 60;
 
final int d2 = 60;
g.drawString(str1, -y - gridHeight + d1, x + pos1);
g.drawString(str2, -y - gridHeight + d2, x + 58);
if (GS1Frame.this.barcodeDatamatrix != null) {
float ratioDatamatrix = 15f;
final int wd = (int) (GS1Frame.this.barcodeDatamatrix.getWidth() / ratioDatamatrix);
final int hd = (int) (GS1Frame.this.barcodeDatamatrix.getHeight() / ratioDatamatrix);
g.drawImage(GS1Frame.this.barcodeDatamatrix, -y - 10 - wd, x + 10, wd, hd, null);
final int w1 = GS1Frame.this.barcodeImage.getWidth() / 4;
final int h1 = GS1Frame.this.barcodeImage.getHeight() / 4;
g.drawImage(GS1Frame.this.barcodeImage, -y + -gridHeight + 60, x + 70, w1, h1, null);
 
}
if (GS1Frame.this.barcodeImageBatch != null) {
// GS1-128 numero de lot
final int w2 = GS1Frame.this.barcodeImageBatch.getWidth() / 4;
final int h2 = GS1Frame.this.barcodeImageBatch.getHeight() / 4;
g.drawImage(GS1Frame.this.barcodeImageBatch, -y - gridHeight + 60, x + 180, w2, h2, null);
}
g2d.setTransform(defaultAt);
 
}
};
 
}
 
public JPanel createToolBar() {
final JPanel toolbar = new JPanel();
toolbar.setOpaque(false);
toolbar.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(2, 3, 2, 2);
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
c.gridwidth = 1;
toolbar.add(new JLabel("Etiquettes par page"), c);
c.gridx++;
 
twoPerPage.setOpaque(false);
fourPerPage.setOpaque(false);
final ButtonGroup g = new ButtonGroup();
g.add(twoPerPage);
g.add(fourPerPage);
 
if (properties.getProperty("labelsPerPage", "4").equals("4")) {
fourPerPage.setSelected(true);
} else {
twoPerPage.setSelected(true);
}
 
toolbar.add(twoPerPage, c);
c.gridx++;
toolbar.add(fourPerPage, c);
c.gridx++;
checkIgnoreMargins.setOpaque(false);
checkIgnoreMargins.setSelected(properties.getProperty("ignoreMargin", "false").equals("true"));
c.gridx++;
toolbar.add(checkIgnoreMargins, c);
c.gridx++;
c.weightx = 1;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.NORTHEAST;
final JButton printButton = new JButton("Imprimer");
printButton.setOpaque(false);
toolbar.add(printButton, c);
c.gridx++;
 
twoPerPage.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
if (twoPerPage.isSelected()) {
GS1Frame.this.labelPanel.setColumnCount(1);
} else {
GS1Frame.this.labelPanel.setColumnCount(2);
}
 
}
});
 
fourPerPage.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
if (twoPerPage.isSelected()) {
GS1Frame.this.labelPanel.setColumnCount(1);
} else {
GS1Frame.this.labelPanel.setColumnCount(2);
}
 
}
});
 
printButton.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
if (!isEANValid()) {
JOptionPane.showMessageDialog(GS1Frame.this, "EAN invalide");
return;
}
 
final PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintable(GS1Frame.this.labelPanel);
boolean ok = job.printDialog();
if (ok) {
try {
job.print();
} catch (PrinterException ex) {
JOptionPane.showMessageDialog(GS1Frame.this, "Print error :" + ex.getMessage());
}
}
 
}
});
checkIgnoreMargins.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
GS1Frame.this.labelPanel.setIgnoreMargins(checkIgnoreMargins.isSelected());
 
}
});
return toolbar;
}
 
public JPanel createToolBar2() {
final JPanel toolbar = new JPanel();
toolbar.setOpaque(false);
toolbar.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(2, 3, 2, 2);
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
c.gridwidth = 1;
c.fill = GridBagConstraints.NONE;
toolbar.add(new JLabel("Marge en haut (mm)"), c);
c.gridx++;
final JSpinner sLines = new JSpinner(new SpinnerNumberModel(0, 0, 200, 1));
toolbar.add(sLines, c);
c.gridx++;
toolbar.add(new JLabel("Marge à gauche (mm)"), c);
c.gridx++;
final JSpinner sColums = new JSpinner(new SpinnerNumberModel(0, 0, 200, 1));
c.weightx = 1;
toolbar.add(sColums, c);
c.gridx++;
 
sLines.addChangeListener(new ChangeListener() {
 
@Override
public void stateChanged(ChangeEvent e) {
final Number n = (Number) sLines.getValue();
if (n != null) {
GS1Frame.this.labelPanel.setTopMargin(n.intValue());
}
}
});
sColums.addChangeListener(new ChangeListener() {
 
@Override
public void stateChanged(ChangeEvent e) {
final Number n = (Number) sColums.getValue();
if (n != null) {
GS1Frame.this.labelPanel.setLeftMargin(n.intValue());
}
}
});
 
return toolbar;
}
 
public String readZPLScript() {
if (new File("zpl.txt").exists()) {
try (InputStream in = new FileInputStream(new File("zpl.txt"))) {
return readAscii(in);
} catch (Exception e) {
e.printStackTrace();
}
} else {
try (InputStream in = GS1Frame.class.getResourceAsStream("default-zpl.txt")) {
return readAscii(in);
} catch (Exception e) {
e.printStackTrace();
}
 
}
return "";
}
 
protected String createZPLCode() {
final String script = readZPLScript();
final BufferedReader reader = new BufferedReader(new StringReader(script));
final StringBuilder builder = new StringBuilder();
final GS1Util u = new GS1Util();
try {
String line = reader.readLine();
while (line != null) {
if (line.contains("XXXXXXXXXX")) {
if (!this.textSupplier.getText().isEmpty()) {
line = line.replace("XXXXXXXXXX", this.textSupplier.getText());
builder.append(line);
builder.append("\r\n");
}
} else if (line.contains("YYYYYYYYYY")) {
line = line.replace("YYYYYYYYYY", this.textName.getText());
builder.append(line);
builder.append("\r\n");
} else if (line.contains("(02)7612345678900(15)201218(37)9999")) {
line = line.replace("(02)7612345678900(15)201218(37)9999", getGS128().formatHumanReadable());
builder.append(line);
builder.append("\r\n");
 
} else if (line.contains("(10)LLLLLLLLL")) {
GS1AIElements batch = getGS128Batch();
if (!batch.isEmpty()) {
line = line.replace("(10)LLLLLLLLL", batch.formatHumanReadable());
builder.append(line);
builder.append("\r\n");
}
} else if (line.contains("OPENCONCERTO")) {
GS1AIElements all = getGS1();
line = line.replace("OPENCONCERTO", u.formatZPL(all));
builder.append(line);
builder.append("\r\n");
 
} else {
builder.append(line);
builder.append("\r\n");
}
 
line = reader.readLine();
}
} catch (Exception e) {
e.printStackTrace();
}
 
return builder.toString();
}
 
private GS1AIElements getGS1() {
final GS1AIElements gs1 = new GS1AIElements();
final String ean = this.textEAN.getText().trim();
if (!ean.isEmpty()) {
if (ean.length() == 13 || (ean.length() == 14 && ean.charAt(0) == '0')) {
if (ean.length() == 13) {
gs1.put("02", '0' + ean);
} else {
gs1.put("02", ean);
}
} else {
gs1.put("01", ean);
}
}
 
if (this.dateDLUODLC.getValue() != null) {
String date = this.dfDate.format(this.dateDLUODLC.getValue());
if (this.b1.isSelected()) {
gs1.put("15", date);
} else {
gs1.put("17", date);
}
}
 
final String nbProducts = this.textNumberOfProducts.getText();
if (!nbProducts.trim().isEmpty()) {
int n = Integer.parseInt(this.textNumberOfProducts.getText());
gs1.put("37", String.valueOf(n));
 
}
final String batch = this.textBatch.getText().trim();
if (!batch.isEmpty()) {
gs1.put("10", batch);
}
 
return gs1;
}
 
private GS1AIElements getGS128Batch() {
final GS1AIElements gs1 = new GS1AIElements();
final String batch = this.textBatch.getText().trim();
if (!batch.isEmpty()) {
gs1.put("10", batch);
}
return gs1;
}
 
private GS1AIElements getGS128() {
final GS1AIElements gs1 = getGS1();
gs1.remove("10");
return gs1;
}
 
public String readAscii(final InputStream in) throws IOException {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final byte[] buf = new byte[8192];
int length;
while ((length = in.read(buf)) > 0) {
out.write(buf, 0, length);
}
out.flush();
out.close();
return new String(out.toByteArray(), StandardCharsets.US_ASCII);
}
 
static List<Image> frameIcon;
 
public static synchronized List<Image> getFrameIcon() {
if (frameIcon == null) {
frameIcon = new ArrayList<>();
final int[] sizes = { 16, 32, 48, 96 };
for (int i = 0; i < sizes.length; i++) {
int v = sizes[i];
try {
frameIcon.add(new ImageIcon(GS1Frame.class.getResource(v + ".png")).getImage());
} catch (Exception e) {
e.printStackTrace();
}
}
}
return frameIcon;
}
 
public static void main(String[] args) {
 
if (!new File("zpl.txt").exists()) {
final InputStream in = GS1Frame.class.getResourceAsStream("default-zpl.txt");
try (FileOutputStream fOut = new FileOutputStream(new File("zpl.txt"))) {
byte[] buf = new byte[8192];
int length;
while ((length = in.read(buf)) > 0) {
fOut.write(buf, 0, length);
}
fOut.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
SwingUtilities.invokeLater(new Runnable() {
 
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e1) {
e1.printStackTrace();
}
 
final GS1Frame f = new GS1Frame();
f.setIconImages(getFrameIcon());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
f.setLocationRelativeTo(null);
f.setResizable(false);
 
}
});
 
}
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/LabelRenderer.java
2,8 → 2,6
 
import java.awt.Graphics;
 
import org.openconcerto.sql.model.SQLRowAccessor;
 
public interface LabelRenderer {
public void paintLabel(Graphics g, SQLRowAccessor row, int x, int y, int gridWith, int gridHeight, float fontSize);
public void paintLabel(Graphics g, Label label, int x, int y, int gridWith, int gridHeight, float fontSize);
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1Util.java
New file
0,0 → 1,243
package org.openconcerto.modules.label.gs1;
 
import org.openconcerto.modules.label.ISO646;
 
public class GS1Util {
 
// ISO/IEC 15424 symbology identifiers
// ]E0 : EAN-13, UPC-A, or UPC-E : 13 digits
// ]E1 : Two-digit add-on symbol : 2 digits
// ]E2 : Five-digit add-on symbol : 5 digits
// ]E3 : EAN-13, UPC-A, or UPC-E with add-on symbol : 15 or 18 digits
// ]E4 : EAN-8 : 8 digits
// ]I1 : ITF-14 : 14 digits
// ]C1 : GS1-128 : Standard AI element strings
private static final String GS1_128_SCANNER_PREFIX = "]C1";
// ]e0 : GS1 DataBar : Standard AI element strings
private static final String GS1_DATABAR_SCANNER_PREFIX = "]e0";
// ]e1 : GS1 Composite : Data packet containing the data + an encoded symbol separator
// character.
// ]e2 : GS1 Composite : Data packet containing the data following an escape mechanism
// character.
// ]d2 : GS1 DataMatrix : Standard AI element strings
private static final String GS1_DATAMATRIX_SCANNER_PREFIX = "]d2";
// ]Q3 : GS1 QR Code : Standard AI element strings
private static final String GS1_QRCODE_SCANNER_PREFIX = "]Q3";
// ]J1 : GS1 DotCode : Standard AI element strings
 
static final int ERROR_CODE_INVALID_GS1_SCAN = 0;
static final int ERROR_CODE_UNKNOWN_AI = 1;
static final int ERROR_CODE_INCOMPLETE_AI = 2;
static final int ERROR_CODE_NOT_FOUND_SEPARATOR = 3;
static final int ERROR_CODE_INSUFFICIENT_VALUE_LENGTH = 4;
static final int ERROR_CODE_EXCEEDED_VALUE_LENGTH = 5;
static final int ERROR_CODE_CONVERT_DECIMAL_POINT = 5;
static final int ERROR_CODE_WRONG_DECIMAL_POINT = 6;
static final int ERROR_CODE_CONVERT_DECIMAL_VALUE = 7;
 
public static final char GS_SEPARATOR = '\u001D';
// public static final char FNC1_SEPARATOR = 232;
 
static final int SPACE_SEPARATOR = ' ';
 
int separator;
 
public GS1Util() {
this(GS_SEPARATOR);
}
 
public GS1Util(int separator) {
this.separator = separator;
}
 
public GS1AIElements parseFromScanner(String scan) throws GS1ParseException {
if (scan.startsWith(GS1_DATAMATRIX_SCANNER_PREFIX) || scan.startsWith(GS1_128_SCANNER_PREFIX) || scan.startsWith(GS1_DATABAR_SCANNER_PREFIX) || scan.startsWith(GS1_QRCODE_SCANNER_PREFIX)) {
return parse(scan.substring(3));
}
return parse(scan);
}
 
public GS1AIElements parse(String barcode) throws GS1ParseException {
if (barcode.length() < 3) {
throw new GS1ParseException("", ERROR_CODE_INVALID_GS1_SCAN, "code too short");
}
 
System.err.println("GS1Util.parse()" + barcode);
GS1AIElements attr = new GS1AIElements();
StringBuilder ai = new StringBuilder();
 
int length = barcode.length();
for (int i = 0; i < length; ++i) {
int aiLength = ai.length();
if (aiLength > 1) {
GS1ApplicationIdentifier aii = GS1AIElements.getApplicationIdentifier(ai.toString());
if (aii == null) {
if (aiLength < 4)
ai.append(barcode.charAt(i));
else
throw new GS1ParseException(ai.toString(), ERROR_CODE_UNKNOWN_AI, "Unknown AI");
} else {
int decimalPoint = 0;
if (aii.decimalPoint) {
try {
decimalPoint = Integer.valueOf(String.valueOf(barcode.charAt(i)));
} catch (NumberFormatException e) {
throw new GS1ParseException(ai.toString(), ERROR_CODE_CONVERT_DECIMAL_POINT, "Errow convert to decimal point");
}
 
if (++i >= length)
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length");
}
 
String value;
 
if (aii.variableLength) {
int separatorIndex = barcode.indexOf(this.separator, i);
 
if (separatorIndex < 0) {
if (length - i > aii.length)
throw new GS1ParseException(ai.toString(), ERROR_CODE_NOT_FOUND_SEPARATOR, "Not found separator");
else if (length - i < aii.minLength)
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length");
else {
value = barcode.substring(i);
i = length;
}
} else if (separatorIndex - i > aii.length)
throw new GS1ParseException(ai.toString(), ERROR_CODE_EXCEEDED_VALUE_LENGTH, "Exceeded value length");
else if (separatorIndex - i < aii.minLength)
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length");
else {
value = barcode.substring(i, separatorIndex);
i = separatorIndex;
}
} else {
if (i + aii.length > length) {
throw new GS1ParseException(ai.toString(), ERROR_CODE_INSUFFICIENT_VALUE_LENGTH, "Insufficient value length");
}
value = barcode.substring(i, i + aii.length);
i += aii.length - 1;
}
 
if (aii.decimalPoint && decimalPoint > 0) {
if (decimalPoint >= value.length())
throw new GS1ParseException(ai.toString(), ERROR_CODE_WRONG_DECIMAL_POINT, "Decimal point more then value length");
 
try {
value = String.valueOf(Double.valueOf(value.substring(0, value.length() - decimalPoint) + "." + value.substring(value.length() - decimalPoint)));
} catch (NumberFormatException e) {
throw new GS1ParseException(ai.toString(), ERROR_CODE_CONVERT_DECIMAL_VALUE, "Error convert decimal point value");
}
}
 
attr.put(ai.toString(), value);
 
ai.setLength(0);
}
} else
ai.append(barcode.charAt(i));
}
 
if (ai.length() > 0)
throw new GS1ParseException(ai.toString(), ERROR_CODE_INCOMPLETE_AI, "Incomplete AI");
 
return attr;
}
 
public String format(GS1AIElements values) {
 
StringBuilder b = new StringBuilder();
int size = values.size();
for (int i = 0; i < size; i++) {
String k = values.getKey(i);
GS1ApplicationIdentifier ai = GS1AIElements.getApplicationIdentifier(k);
final String value = values.getValue(i);
b.append(k);
b.append(value);
if (ai.variableLength && i < size - 1) {
b.append((char) this.separator);
}
}
return b.toString();
}
 
public String formatZPL(GS1AIElements values) {
 
StringBuilder b = new StringBuilder();
int size = values.size();
b.append("@1");
for (int i = 0; i < size; i++) {
String k = values.getKey(i);
GS1ApplicationIdentifier ai = GS1AIElements.getApplicationIdentifier(k);
final String value = values.getValue(i);
b.append(k);
b.append(value);
if (ai.variableLength && i < size - 1) {
b.append("@d029");
}
}
return b.toString();
}
 
public String formatDataMatrix(GS1AIElements values) {
 
StringBuilder b = new StringBuilder();
int size = values.size();
// b.append(FNC1_SEPARATOR);
for (int i = 0; i < size; i++) {
String k = values.getKey(i);
GS1ApplicationIdentifier ai = GS1AIElements.getApplicationIdentifier(k);
final String value = values.getValue(i);
b.append('[');
b.append(k);
b.append(']');
b.append(value);
if (ai.variableLength && i < size - 1) {
// b.append(GS_SEPARATOR);
}
}
return b.toString();
}
 
public static void main(String[] args) throws GS1ParseException {
GS1Util p = new GS1Util('_');
System.out.println("GS1Util.main()" + (char) p.separator);
String barcode = "0104607018700852111806051718062910180605_211";
// barcode = "01088888931021461712031510W1040190";
GS1AIElements values = p.parse(barcode);
values.dump(System.out);
 
values.put("391", "kookkk");
System.out.println(p.format(values));
System.out.println(values.formatHumanReadable());
 
GS1AIElements valuesMax = new GS1AIElements();
valuesMax.put("02", "01234567891234");
valuesMax.put("15", "201202");
valuesMax.put("37", "12345678");
valuesMax.put("10", "12345678901234567890");
GS1Util pStd = new GS1Util();
System.out.println(p.format(valuesMax));
System.out.println(p.format(valuesMax).length());
System.out.println(pStd.format(valuesMax));
System.out.println(pStd.format(valuesMax).length());
System.err.println("GS1Util.main() GS128 from barcode reader");
String gs1128 = "]C10207612345678900152012153745646578";
GS1Util p2 = new GS1Util();
values = p2.parseFromScanner(gs1128);
values.dump(System.out);
}
 
public static String showAllChars(String s) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (ISO646.isValid(s.charAt(i))) {
b.append(s.charAt(i));
} else {
b.append("[" + (int) s.charAt(i) + "]");
}
}
return b.toString();
}
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1AIElements.java
New file
0,0 → 1,259
package org.openconcerto.modules.label.gs1;
 
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
 
public class GS1AIElements {
static final HashMap<String, GS1ApplicationIdentifier> GS1_128_AI = new HashMap<>();
static {
GS1_128_AI.put("00", new GS1ApplicationIdentifier(0, 18, false, false));
GS1_128_AI.put("01", new GS1ApplicationIdentifier(0, 14, false, false));
GS1_128_AI.put("02", new GS1ApplicationIdentifier(0, 14, false, false));
GS1_128_AI.put("10", new GS1ApplicationIdentifier(0, 20, true, false));
GS1_128_AI.put("11", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("12", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("13", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("14", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("15", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("17", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("20", new GS1ApplicationIdentifier(0, 2, false, false));
GS1_128_AI.put("21", new GS1ApplicationIdentifier(0, 20, true, false));
GS1_128_AI.put("240", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("241", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("242", new GS1ApplicationIdentifier(0, 6, true, false));
GS1_128_AI.put("250", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("251", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("253", new GS1ApplicationIdentifier(13, 30, true, false));
GS1_128_AI.put("254", new GS1ApplicationIdentifier(0, 20, true, false));
GS1_128_AI.put("255", new GS1ApplicationIdentifier(13, 25, true, false));
GS1_128_AI.put("30", new GS1ApplicationIdentifier(0, 8, true, false));
GS1_128_AI.put("310", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("311", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("312", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("313", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("314", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("315", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("316", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("320", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("321", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("322", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("323", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("324", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("325", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("326", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("327", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("328", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("329", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("330", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("331", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("332", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("333", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("334", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("335", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("336", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("340", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("341", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("342", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("343", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("344", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("345", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("346", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("347", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("348", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("349", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("350", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("351", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("352", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("353", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("354", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("355", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("356", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("357", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("360", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("361", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("362", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("363", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("364", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("365", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("366", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("367", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("368", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("369", new GS1ApplicationIdentifier(0, 6, false, true));
GS1_128_AI.put("37", new GS1ApplicationIdentifier(0, 8, true, false));
GS1_128_AI.put("390", new GS1ApplicationIdentifier(0, 15, true, true));
GS1_128_AI.put("391", new GS1ApplicationIdentifier(3, 18, true, true));
GS1_128_AI.put("392", new GS1ApplicationIdentifier(0, 15, true, true));
GS1_128_AI.put("393", new GS1ApplicationIdentifier(3, 18, true, true));
GS1_128_AI.put("400", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("401", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("402", new GS1ApplicationIdentifier(0, 17, false, false));
GS1_128_AI.put("403", new GS1ApplicationIdentifier(3, 30, true, false));
GS1_128_AI.put("410", new GS1ApplicationIdentifier(0, 17, false, false));
GS1_128_AI.put("411", new GS1ApplicationIdentifier(0, 17, false, false));
GS1_128_AI.put("412", new GS1ApplicationIdentifier(0, 17, false, false));
GS1_128_AI.put("413", new GS1ApplicationIdentifier(0, 17, false, false));
GS1_128_AI.put("414", new GS1ApplicationIdentifier(0, 17, false, false));
GS1_128_AI.put("420", new GS1ApplicationIdentifier(0, 20, true, false));
GS1_128_AI.put("421", new GS1ApplicationIdentifier(0, 0, true, false));
GS1_128_AI.put("422", new GS1ApplicationIdentifier(0, 3, false, false));
GS1_128_AI.put("423", new GS1ApplicationIdentifier(3, 15, true, false));
GS1_128_AI.put("424", new GS1ApplicationIdentifier(0, 3, false, false));
GS1_128_AI.put("425", new GS1ApplicationIdentifier(0, 3, false, false));
GS1_128_AI.put("426", new GS1ApplicationIdentifier(0, 3, false, false));
GS1_128_AI.put("7001", new GS1ApplicationIdentifier(0, 13, false, false));
GS1_128_AI.put("7002", new GS1ApplicationIdentifier(0, 30, false, false));
GS1_128_AI.put("7003", new GS1ApplicationIdentifier(0, 10, false, false));
GS1_128_AI.put("7004", new GS1ApplicationIdentifier(0, 4, true, false));
GS1_128_AI.put("8001", new GS1ApplicationIdentifier(0, 14, false, false));
GS1_128_AI.put("8002", new GS1ApplicationIdentifier(0, 20, true, false));
GS1_128_AI.put("8003", new GS1ApplicationIdentifier(14, 30, true, false));
GS1_128_AI.put("8004", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("8005", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("8006", new GS1ApplicationIdentifier(0, 18, false, false));
GS1_128_AI.put("8007", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("8008", new GS1ApplicationIdentifier(8, 12, true, false));
GS1_128_AI.put("8018", new GS1ApplicationIdentifier(0, 18, false, false));
GS1_128_AI.put("8020", new GS1ApplicationIdentifier(0, 25, true, false));
GS1_128_AI.put("8100", new GS1ApplicationIdentifier(0, 6, false, false));
GS1_128_AI.put("8101", new GS1ApplicationIdentifier(0, 10, false, false));
GS1_128_AI.put("8102", new GS1ApplicationIdentifier(0, 2, false, false));
GS1_128_AI.put("8110", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("8200", new GS1ApplicationIdentifier(0, 70, true, false));
GS1_128_AI.put("90", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("91", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("92", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("93", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("94", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("95", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("96", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("97", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("98", new GS1ApplicationIdentifier(0, 30, true, false));
GS1_128_AI.put("99", new GS1ApplicationIdentifier(0, 30, true, false));
}
 
private final ArrayList<String> keysAndValues = new ArrayList<>();
 
public GS1AIElements() {
 
}
 
public boolean isEmpty() {
return this.keysAndValues.isEmpty();
}
 
public boolean containsKey(String key) {
final int size = this.keysAndValues.size();
for (int i = 0; i < size; i += 2) {
if (this.keysAndValues.get(i).equals(key)) {
return true;
}
}
return false;
}
 
public boolean containsValue(String value) {
final int size = this.keysAndValues.size();
for (int i = 1; i < size; i += 2) {
if (this.keysAndValues.get(i).equals(value)) {
return true;
}
}
return false;
}
 
public String getKey(int index) {
return this.keysAndValues.get(index * 2);
}
 
public String getValue(int index) {
return this.keysAndValues.get(1 + index * 2);
}
 
public String get(String key) {
final int size = this.keysAndValues.size();
for (int i = 0; i < size; i += 2) {
if (this.keysAndValues.get(i).equals(key)) {
 
return this.keysAndValues.get(i + 1);
}
}
return null;
}
 
public String put(String key, String value) {
GS1ApplicationIdentifier ai = GS1_128_AI.get(key);
if (ai == null) {
throw new IllegalArgumentException("AI " + key + " unknown");
}
if (ai.variableLength) {
if (value.length() < ai.minLength) {
throw new IllegalArgumentException("AI " + key + " value length must >= " + ai.minLength + " but is " + value.length() + " for value " + value);
}
if (value.length() > ai.length) {
throw new IllegalArgumentException("AI " + key + " value length must be <= " + ai.length + " but is " + value.length() + " for value " + value);
}
} else {
if (value.length() != ai.length) {
throw new IllegalArgumentException("AI " + key + " value length must be " + ai.length + " but is " + value.length() + " for value " + value);
}
}
 
final int size = this.keysAndValues.size();
for (int i = 0; i < size; i += 2) {
if (this.keysAndValues.get(i).equals(key)) {
final String old = this.keysAndValues.get(i + 1);
this.keysAndValues.set(i + 1, value);
return old;
}
}
this.keysAndValues.add(key);
this.keysAndValues.add(value);
return null;
}
 
public String remove(String key) {
final int size = this.keysAndValues.size();
for (int i = 0; i < size; i += 2) {
if (this.keysAndValues.get(i).equals(key)) {
this.keysAndValues.remove(i);
return this.keysAndValues.remove(i);
}
}
return null;
}
 
public void clear() {
this.keysAndValues.clear();
}
 
public int size() {
return this.keysAndValues.size() / 2;
}
 
public void dump(PrintStream out) {
for (int i = 0; i < size(); i++) {
out.print("(");
out.print(getKey(i));
out.print(")");
out.println(getValue(i));
}
out.flush();
}
 
public static GS1ApplicationIdentifier getApplicationIdentifier(String k) {
return GS1_128_AI.get(k);
}
 
public String formatHumanReadable() {
StringBuilder b = new StringBuilder();
int size = size();
for (int i = 0; i < size; i++) {
b.append('(');
b.append(this.getKey(i));
b.append(')');
b.append(this.getValue(i));
}
return b.toString();
}
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1ApplicationIdentifier.java
New file
0,0 → 1,17
package org.openconcerto.modules.label.gs1;
 
public class GS1ApplicationIdentifier {
 
public final int minLength;
public final int length;
public final boolean variableLength;
public final boolean decimalPoint;
 
public GS1ApplicationIdentifier(int minLength, int length, boolean variableLength, boolean decimalPoint) {
this.minLength = minLength;
this.length = length;
this.variableLength = variableLength;
this.decimalPoint = decimalPoint;
}
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/gs1/GS1ParseException.java
New file
0,0 → 1,18
package org.openconcerto.modules.label.gs1;
 
public class GS1ParseException extends Exception {
final String ai;
final int errorCode;
final String errorMessage;
 
public GS1ParseException(final String ai, final int errorCode, final String errorMessage) {
this.ai = ai;
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
 
@Override
public String toString() {
return "AI: " + ai + ", errorCode: " + errorCode + ", errorMessage: " + errorMessage;
}
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/graphicspl/GPLRenderer.java
New file
0,0 → 1,166
package org.openconcerto.modules.label.graphicspl;
 
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
 
import javax.imageio.ImageIO;
 
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
 
public abstract class GPLRenderer {
public static final int ALIGN_LEFT = 0;
public static final int ALIGN_RIGHT = 1;
public static final int ALIGN_CENTER = 2;
 
public static final int BARCODE_EAN8 = 0;
public static final int BARCODE_EAN13 = 1;
public static final int BARCODE_CODE128 = 2;
public static final int BARCODE_CODE128_GS1 = 3;
public static final int BARCODE_DATAMATRIX = 4;
public static final int BARCODE_QRCODE = 5;
private final float ratio;
 
public GPLRenderer(float ratio) {
this.ratio = ratio;
}
 
public GPLRenderer() {
this.ratio = 1.0f;
}
 
public float getRatio() {
return ratio;
}
 
public void render(GraphicsPL graphicsPL) throws IOException {
 
Document doc = graphicsPL.getDocument();
final int width = Integer.parseInt(doc.getDocumentElement().getAttribute("width"));
NodeList nodeList = doc.getFirstChild().getChildNodes();
int size = nodeList.getLength();
for (int i = 0; i < size; i++) {
 
if (nodeList.item(i).getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) nodeList.item(i);
String name = element.getNodeName();
if (name.equals("text")) {
int x = Math.round(ratio * Integer.parseInt(element.getAttribute("x")));
int y = Math.round(ratio * Integer.parseInt(element.getAttribute("y")));
String txt = element.getTextContent();
Color color = Color.BLACK;
if (element.hasAttribute("color")) {
color = Color.decode(element.getAttribute("color"));
}
int fontSize = Math.round(ratio * Integer.parseInt(element.getAttribute("fontsize")));
String fontName = element.getAttribute("font");
int maxWidth = width - x;
boolean wrap = false;
int align = ALIGN_LEFT;
if (element.hasAttribute("align")) {
if (element.getAttribute("align").equals("right")) {
align = ALIGN_RIGHT;
} else if (element.getAttribute("align").equals("center")) {
align = ALIGN_CENTER;
}
}
if (!element.hasAttribute("visible") || element.getAttribute("visible").equals("true")) {
drawText(x, y, txt, align, fontName, fontSize, color, maxWidth, wrap);
}
 
} else if (name.equals("rectangle")) {
int x = Math.round(ratio * Integer.parseInt(element.getAttribute("x")));
int y = Math.round(ratio * Integer.parseInt(element.getAttribute("y")));
int w = Math.round(ratio * Integer.parseInt(element.getAttribute("width")));
int h = Math.round(ratio * Integer.parseInt(element.getAttribute("height")));
Color color = Color.BLACK;
if (element.hasAttribute("color")) {
color = Color.decode(element.getAttribute("color"));
}
 
if (element.hasAttribute("fill") && element.getAttribute("fill").equals("true")) {
fillRectangle(x, y, w, h, color);
} else {
int lineWidth = Math.round(ratio);
if (element.hasAttribute("linewidth")) {
lineWidth = Math.round(ratio * Integer.parseInt(element.getAttribute("linewidth")));
}
drawRectangle(x, y, w, h, color, lineWidth);
}
 
} else if (name.equals("image")) {
String fileName = element.getAttribute("file");
int x = Math.round(ratio * Integer.parseInt(element.getAttribute("x")));
int y = Math.round(ratio * Integer.parseInt(element.getAttribute("y")));
BufferedImage img = ImageIO.read(new File(graphicsPL.getImageDir(), fileName));
int w = Math.round(ratio * img.getWidth());
int h = Math.round(ratio * img.getHeight());
if (element.hasAttribute("width")) {
w = Math.round(ratio * Integer.parseInt(element.getAttribute("width")));
}
if (element.hasAttribute("height")) {
h = Math.round(ratio * Integer.parseInt(element.getAttribute("height")));
}
drawImage(x, y, w, h, img);
 
} else if (name.equals("barcode")) {
int x = Math.round(ratio * Integer.parseInt(element.getAttribute("x")));
int y = Math.round(ratio * Integer.parseInt(element.getAttribute("y")));
int h = 0;
 
if (element.hasAttribute("height")) {
h = Math.round(ratio * Integer.parseInt(element.getAttribute("height")));
}
String type = element.getAttribute("type");
String code = element.getTextContent();
int t;
if (type.equals("ean8")) {
t = BARCODE_EAN8;
} else if (type.equals("ean13")) {
t = BARCODE_EAN13;
} else if (type.equals("ean128")) {
t = BARCODE_CODE128;
} else if (type.equals("gs1")) {
t = BARCODE_CODE128_GS1;
} else if (type.equals("datamatrix")) {
t = BARCODE_DATAMATRIX;
if (h != 0) {
System.err.println("ignoring datamatrix height attribute");
}
} else if (type.equals("qrcode")) {
t = BARCODE_QRCODE;
} else {
throw new IllegalArgumentException("unsupported barcode type : " + type);
}
 
int fontSize = Math.round(ratio * 8);
if (element.hasAttribute("fontsize")) {
fontSize = Math.round(ratio * Integer.parseInt(element.getAttribute("fontsize")));
}
int moduleWidth = Math.round(ratio);
if (element.hasAttribute("modulewidth")) {
moduleWidth = Math.round(ratio * Integer.parseInt(element.getAttribute("modulewidth")));
}
drawBarcode(x, y, h, t, code, moduleWidth, fontSize);
} else {
throw new IllegalStateException("unsupported primitive : " + name);
}
}
}
}
 
public abstract void drawText(int x, int y, String text, int align, String fontName, int fontSize, Color color, int maxWidth, boolean wrap);
 
public abstract void drawImage(int x, int y, int w, int h, BufferedImage img);
 
public abstract void fillRectangle(int x, int y, int w, int h, Color color);
 
public abstract void drawRectangle(int x, int y, int w, int h, Color color, int lineWidth);
 
public abstract void drawBarcode(int x, int y, int h, int type, String code, int moduleWidth, int fontSize);
 
}
/trunk/Modules/Module Label/src/org/openconcerto/modules/label/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/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/uk/org/okapibarcode/util/EciMode.java
New file
0,0 → 1,67
/*
* Copyright 2018 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.util;
 
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
 
public class EciMode {
 
public static final EciMode NONE = new EciMode(-1, null);
 
public final int mode;
public final Charset charset;
 
private EciMode(final int mode, final Charset charset) {
this.mode = mode;
this.charset = charset;
}
 
public static EciMode of(final String data, final String charsetName, final int mode) {
try {
final Charset charset = Charset.forName(charsetName);
if (charset.canEncode() && charset.newEncoder().canEncode(data)) {
return new EciMode(mode, charset);
} else {
return NONE;
}
} catch (final UnsupportedCharsetException e) {
return NONE;
}
}
 
public EciMode or(final String data, final String charsetName, final int mode) {
if (!equals(NONE)) {
return this;
} else {
return of(data, charsetName, mode);
}
}
 
@Override
public boolean equals(final Object other) {
return other instanceof EciMode && ((EciMode) other).mode == this.mode;
}
 
@Override
public int hashCode() {
return Integer.valueOf(this.mode).hashCode();
}
 
@Override
public String toString() {
return "EciMode[mode=" + this.mode + ", charset=" + this.charset + "]";
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/util/Strings.java
New file
0,0 → 1,226
/*
* Copyright 2018 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.util;
 
import static java.nio.charset.StandardCharsets.ISO_8859_1;
 
import java.nio.charset.StandardCharsets;
 
import uk.org.okapibarcode.backend.OkapiException;
 
/**
* String utility class.
*
* @author Daniel Gredler
*/
public final class Strings {
 
private Strings() {
// utility class
}
 
/**
* Replaces raw values with special placeholders, where applicable.
*
* @param s the string to add placeholders to
* @return the specified string, with placeholders added
* @see <a href="http://www.zint.org.uk/Manual.aspx?type=p&page=4">Zint placeholders</a>
* @see #unescape(String, boolean)
*/
public static String escape(final String s) {
final StringBuilder sb = new StringBuilder(s.length() + 10);
for (int i = 0; i < s.length(); i++) {
final char c = s.charAt(i);
switch (c) {
case '\u0000':
sb.append("\\0"); // null
break;
case '\u0004':
sb.append("\\E"); // end of transmission
break;
case '\u0007':
sb.append("\\a"); // bell
break;
case '\u0008':
sb.append("\\b"); // backspace
break;
case '\u0009':
sb.append("\\t"); // horizontal tab
break;
case '\n':
sb.append("\\n"); // line feed
break;
case '\u000b':
sb.append("\\v"); // vertical tab
break;
case '\u000c':
sb.append("\\f"); // form feed
break;
case '\r':
sb.append("\\r"); // carriage return
break;
case '\u001b':
sb.append("\\e"); // escape
break;
case '\u001d':
sb.append("\\G"); // group separator
break;
case '\u001e':
sb.append("\\R"); // record separator
break;
case '\\':
sb.append("\\\\"); // escape the escape character
break;
default:
if (c >= 32 && c <= 126) {
sb.append(c); // printable ASCII
} else {
final byte[] bytes = String.valueOf(c).getBytes(ISO_8859_1);
final String hex = String.format("%02X", bytes[0] & 0xFF);
sb.append("\\x").append(hex);
}
break;
}
}
return sb.toString();
}
 
/**
* Replaces any special placeholders with their raw values (not including FNC values).
*
* @param s the string to check for placeholders
* @param lenient whether or not to be lenient with unrecognized escape sequences
* @return the specified string, with placeholders replaced
* @see <a href="http://www.zint.org.uk/Manual.aspx?type=p&page=4">Zint placeholders</a>
* @see #escape(String)
*/
public static String unescape(final String s, final boolean lenient) {
final StringBuilder sb = new StringBuilder(s.length());
for (int i = 0; i < s.length(); i++) {
final char c = s.charAt(i);
if (c != '\\') {
sb.append(c);
} else {
if (i + 1 >= s.length()) {
final String msg = "Error processing escape sequences: expected escape character, found end of string";
throw new OkapiException(msg);
} else {
final char c2 = s.charAt(i + 1);
switch (c2) {
case '0':
sb.append('\u0000'); // null
i++;
break;
case 'E':
sb.append('\u0004'); // end of transmission
i++;
break;
case 'a':
sb.append('\u0007'); // bell
i++;
break;
case 'b':
sb.append('\u0008'); // backspace
i++;
break;
case 't':
sb.append('\u0009'); // horizontal tab
i++;
break;
case 'n':
sb.append('\n'); // line feed
i++;
break;
case 'v':
sb.append('\u000b'); // vertical tab
i++;
break;
case 'f':
sb.append('\u000c'); // form feed
i++;
break;
case 'r':
sb.append('\r'); // carriage return
i++;
break;
case 'e':
sb.append('\u001b'); // escape
i++;
break;
case 'G':
sb.append('\u001d'); // group separator
i++;
break;
case 'R':
sb.append('\u001e'); // record separator
i++;
break;
case '\\':
sb.append('\\'); // escape the escape character
i++;
break;
case 'x':
if (i + 3 >= s.length()) {
final String msg = "Error processing escape sequences: expected hex sequence, found end of string";
throw new OkapiException(msg);
} else {
final char c3 = s.charAt(i + 2);
final char c4 = s.charAt(i + 3);
if (isHex(c3) && isHex(c4)) {
final byte b = (byte) Integer.parseInt("" + c3 + c4, 16);
sb.append(new String(new byte[] { b }, StandardCharsets.ISO_8859_1));
i += 3;
} else {
final String msg = "Error processing escape sequences: expected hex sequence, found '" + c3 + c4 + "'";
throw new OkapiException(msg);
}
}
break;
default:
if (lenient) {
sb.append(c);
} else {
throw new OkapiException("Error processing escape sequences: expected valid escape character, found '" + c2 + "'");
}
}
}
}
}
return sb.toString();
}
 
private static boolean isHex(final char c) {
return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f';
}
 
/**
* Appends the specific integer to the specified string, in binary format, padded to the
* specified number of digits.
*
* @param s the string to append to
* @param value the value to append, in binary format
* @param digits the number of digits to pad to
*/
public static void binaryAppend(final StringBuilder s, final int value, final int digits) {
final int start = 0x01 << digits - 1;
for (int i = 0; i < digits; i++) {
if ((value & start >> i) == 0) {
s.append('0');
} else {
s.append('1');
}
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/util/Gs1.java
New file
0,0 → 1,596
/*
* Copyright 2018 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.util;
 
import uk.org.okapibarcode.backend.OkapiException;
 
/**
* GS1 utility class.
*/
public final class Gs1 {
 
private Gs1() {
// utility class
}
 
/**
* Verifies that the specified data is in good GS1 format <tt>"[AI]data"</tt> pairs, and returns
* a reduced version of the input string containing FNC1 escape sequences instead of AI
* brackets. With a few small exceptions, this code matches the Zint GS1 validation code as
* closely as possible, in order to make it easier to keep in sync.
*
* @param s the data string to verify
* @param fnc1 the string to use to represent FNC1 in the output
* @return the input data, verified and with FNC1 strings added at the appropriate positions
* @see <a href="https://sourceforge.net/p/zint/code/ci/master/tree/backend/gs1.c">Corresponding
* Zint code</a>
* @see <a href="http://www.gs1.org/docs/gsmp/barcodes/GS1_General_Specifications.pdf">GS1
* specification</a>
*/
public static String verify(final String s, final String fnc1) {
 
// Enforce compliance with GS1 General Specification
// http://www.gs1.org/docs/gsmp/barcodes/GS1_General_Specifications.pdf
 
final char[] source = s.toCharArray();
final StringBuilder reduced = new StringBuilder(source.length);
final int[] ai_value = new int[100];
final int[] ai_location = new int[100];
final int[] data_location = new int[100];
final int[] data_length = new int[100];
int error_latch;
 
/* Detect extended ASCII characters */
for (int i = 0; i < source.length; i++) {
if (source[i] >= 128) {
throw new OkapiException("Extended ASCII characters are not supported by GS1");
}
if (source[i] < 32) {
throw new OkapiException("Control characters are not supported by GS1");
}
}
 
/* Make sure we start with an AI */
if (source[0] != '[') {
throw new OkapiException("Data does not start with an AI");
}
 
/* Check the position of the brackets */
int bracket_level = 0;
int max_bracket_level = 0;
int ai_length = 0;
int max_ai_length = 0;
int min_ai_length = 5;
int j = 0;
boolean ai_latch = false;
for (int i = 0; i < source.length; i++) {
ai_length += j;
if (j == 1 && source[i] != ']' && (source[i] < '0' || source[i] > '9')) {
ai_latch = true;
}
if (source[i] == '[') {
bracket_level++;
j = 1;
}
if (source[i] == ']') {
bracket_level--;
if (ai_length < min_ai_length) {
min_ai_length = ai_length;
}
j = 0;
ai_length = 0;
}
if (bracket_level > max_bracket_level) {
max_bracket_level = bracket_level;
}
if (ai_length > max_ai_length) {
max_ai_length = ai_length;
}
}
min_ai_length--;
 
if (bracket_level != 0) {
/* Not all brackets are closed */
throw new OkapiException("Malformed AI in input data (brackets don't match)");
}
 
if (max_bracket_level > 1) {
/* Nested brackets */
throw new OkapiException("Found nested brackets in input data");
}
 
if (max_ai_length > 4) {
/* AI is too long */
throw new OkapiException("Invalid AI in input data (AI too long)");
}
 
if (min_ai_length <= 1) {
/* AI is too short */
throw new OkapiException("Invalid AI in input data (AI too short)");
}
 
if (ai_latch) {
/* Non-numeric data in AI */
throw new OkapiException("Invalid AI in input data (non-numeric characters in AI)");
}
 
int ai_count = 0;
for (int i = 1; i < source.length; i++) {
if (source[i - 1] == '[') {
ai_location[ai_count] = i;
ai_value[ai_count] = 0;
for (j = 0; source[i + j] != ']'; j++) {
ai_value[ai_count] *= 10;
ai_value[ai_count] += Character.getNumericValue(source[i + j]);
}
ai_count++;
}
}
 
for (int i = 0; i < ai_count; i++) {
data_location[i] = ai_location[i] + 3;
if (ai_value[i] >= 100) {
data_location[i]++;
}
if (ai_value[i] >= 1000) {
data_location[i]++;
}
data_length[i] = source.length - data_location[i];
for (j = source.length - 1; j >= data_location[i]; j--) {
if (source[j] == '[') {
data_length[i] = j - data_location[i];
}
}
}
 
for (int i = 0; i < ai_count; i++) {
if (data_length[i] == 0) {
/* No data for given AI */
throw new OkapiException("Empty data field in input data");
}
}
 
// Check for valid AI values and data lengths according to GS1 General
// Specification Release 18, January 2018
for (int i = 0; i < ai_count; i++) {
 
error_latch = 2;
switch (ai_value[i]) {
// Length 2 Fixed
case 20: // VARIANT
if (data_length[i] != 2) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 3 Fixed
case 422: // ORIGIN
case 424: // COUNTRY PROCESS
case 426: // COUNTRY FULL PROCESS
if (data_length[i] != 3) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 4 Fixed
case 8111: // POINTS
if (data_length[i] != 4) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 6 Fixed
case 11: // PROD DATE
case 12: // DUE DATE
case 13: // PACK DATE
case 15: // BEST BY
case 16: // SELL BY
case 17: // USE BY
case 7006: // FIRST FREEZE DATE
case 8005: // PRICE PER UNIT
if (data_length[i] != 6) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 10 Fixed
case 7003: // EXPIRY TIME
if (data_length[i] != 10) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 13 Fixed
case 410: // SHIP TO LOC
case 411: // BILL TO
case 412: // PURCHASE FROM
case 413: // SHIP FOR LOC
case 414: // LOC NO
case 415: // PAY TO
case 416: // PROD/SERV LOC
case 7001: // NSN
if (data_length[i] != 13) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 14 Fixed
case 1: // GTIN
case 2: // CONTENT
case 8001: // DIMENSIONS
if (data_length[i] != 14) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 17 Fixed
case 402: // GSIN
if (data_length[i] != 17) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 18 Fixed
case 0: // SSCC
case 8006: // ITIP
case 8017: // GSRN PROVIDER
case 8018: // GSRN RECIPIENT
if (data_length[i] != 18) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 2 Max
case 7010: // PROD METHOD
if (data_length[i] > 2) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 3 Max
case 427: // ORIGIN SUBDIVISION
case 7008: // AQUATIC SPECIES
if (data_length[i] > 3) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 4 Max
case 7004: // ACTIVE POTENCY
if (data_length[i] > 4) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 6 Max
case 242: // MTO VARIANT
if (data_length[i] > 6) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 8 Max
case 30: // VAR COUNT
case 37: // COUNT
if (data_length[i] > 8) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 10 Max
case 7009: // FISHING GEAR TYPE
case 8019: // SRIN
if (data_length[i] > 10) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 12 Max
case 7005: // CATCH AREA
case 8011: // CPID SERIAL
if (data_length[i] > 12) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 20 Max
case 10: // BATCH/LOT
case 21: // SERIAL
case 22: // CPV
case 243: // PCN
case 254: // GLN EXTENSION COMPONENT
case 420: // SHIP TO POST
case 7020: // REFURB LOT
case 7021: // FUNC STAT
case 7022: // REV STAT
case 710: // NHRN PZN
case 711: // NHRN CIP
case 712: // NHRN CN
case 713: // NHRN DRN
case 714: // NHRN AIM
case 8002: // CMT NO
case 8012: // VERSION
if (data_length[i] > 20) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 25 Max
case 8020: // REF NO
if (data_length[i] > 25) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 30 Max
case 240: // ADDITIONAL ID
case 241: // CUST PART NO
case 250: // SECONDARY SERIAL
case 251: // REF TO SOURCE
case 400: // ORDER NUMBER
case 401: // GINC
case 403: // ROUTE
case 7002: // MEAT CUT
case 7023: // GIAI ASSEMBLY
case 8004: // GIAI
case 8010: // CPID
case 8013: // BUDI-DI
case 90: // INTERNAL
if (data_length[i] > 30) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 34 Max
case 8007: // IBAN
if (data_length[i] > 34) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
// Length 70 Max
case 8110: // Coupon code
case 8112: // Paperless coupon code
case 8200: // PRODUCT URL
if (data_length[i] > 70) {
error_latch = 1;
} else {
error_latch = 0;
}
break;
 
}
 
if (ai_value[i] == 253) { // GDTI
if (data_length[i] < 14 || data_length[i] > 30) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] == 255) { // GCN
if (data_length[i] < 14 || data_length[i] > 25) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] >= 3100 && ai_value[i] <= 3169) {
if (data_length[i] != 6) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] >= 3200 && ai_value[i] <= 3379) {
if (data_length[i] != 6) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] >= 3400 && ai_value[i] <= 3579) {
if (data_length[i] != 6) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] >= 3600 && ai_value[i] <= 3699) {
if (data_length[i] != 6) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] >= 3900 && ai_value[i] <= 3909) { // AMOUNT
if (data_length[i] > 15) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] >= 3910 && ai_value[i] <= 3919) { // AMOUNT
if (data_length[i] < 4 || data_length[i] > 18) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] >= 3920 && ai_value[i] <= 3929) { // PRICE
if (data_length[i] > 15) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] >= 3930 && ai_value[i] <= 3939) { // PRICE
if (data_length[i] < 4 || data_length[i] > 18) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] >= 3940 && ai_value[i] <= 3949) { // PRCNT OFF
if (data_length[i] != 4) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] == 421) { // SHIP TO POST
if (data_length[i] < 3 || data_length[i] > 12) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] == 423 || ai_value[i] == 425) {
// COUNTRY INITIAL PROCESS || COUNTRY DISASSEMBLY
if (data_length[i] < 3 || data_length[i] > 15) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] == 7007) { // HARVEST DATE
if (data_length[i] < 6 || data_length[i] > 12) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] >= 7030 && ai_value[i] <= 7039) { // PROCESSOR #
if (data_length[i] < 4 || data_length[i] > 30) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] == 8003) { // GRAI
if (data_length[i] < 15 || data_length[i] > 30) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] == 8008) { // PROD TIME
if (data_length[i] < 9 || data_length[i] > 12) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (ai_value[i] >= 91 && ai_value[i] <= 99) { // INTERNAL
if (data_length[i] > 90) {
error_latch = 1;
} else {
error_latch = 0;
}
}
 
if (error_latch == 1) {
throw new OkapiException("Invalid data length for AI");
}
 
if (error_latch == 2) {
throw new OkapiException("Invalid AI value");
}
}
 
/* Resolve AI data - put resulting string in 'reduced' */
int last_ai = 0;
boolean fixedLengthAI = true;
for (int i = 0; i < source.length; i++) {
if (source[i] != '[' && source[i] != ']') {
reduced.append(source[i]);
}
if (source[i] == '[') {
/* Start of an AI string */
if (!fixedLengthAI) {
reduced.append(fnc1);
}
last_ai = 10 * Character.getNumericValue(source[i + 1]) + Character.getNumericValue(source[i + 2]);
/*
* The following values from
* "GS-1 General Specification version 8.0 issue 2, May 2008" figure 5.4.8.2.1 - 1
* "Element Strings with Pre-Defined Length Using Application Identifiers"
*/
fixedLengthAI = last_ai >= 0 && last_ai <= 4 || last_ai >= 11 && last_ai <= 20 || last_ai == 23
|| /* legacy support - see 5.3.8.2.2 */
last_ai >= 31 && last_ai <= 36 || last_ai == 41;
}
}
 
return reduced.toString();
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/util/Doubles.java
New file
0,0 → 1,39
/*
* Copyright 2015 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.util;
 
/**
* Double utility class.
*
* @author Daniel Gredler
*/
public final class Doubles {
 
private Doubles() {
// utility class
}
 
/**
* It's usually not a good idea to check floating point numbers for exact equality. This method
* allows us to check for approximate equality.
*
* @param d1 the first double
* @param d2 the second double
* @return whether or not the two doubles are approximately equal (to within 0.0001)
*/
public static boolean roughlyEqual(final double d1, final double d2) {
return Math.abs(d1 - d2) < 0.0001;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/util/Arrays.java
New file
0,0 → 1,112
/*
* Copyright 2018 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.util;
 
import uk.org.okapibarcode.backend.OkapiException;
 
/**
* Array utility class.
*
* @author Daniel Gredler
*/
public final class Arrays {
 
private Arrays() {
// utility class
}
 
/**
* Returns the position of the specified value in the specified array.
*
* @param value the value to search for
* @param array the array to search in
* @return the position of the specified value in the specified array
*/
public static int positionOf(final char value, final char[] array) {
for (int i = 0; i < array.length; i++) {
if (value == array[i]) {
return i;
}
}
throw new OkapiException("Unable to find character '" + value + "' in character array.");
}
 
/**
* Returns the position of the specified value in the specified array.
*
* @param value the value to search for
* @param array the array to search in
* @return the position of the specified value in the specified array
*/
public static int positionOf(final int value, final int[] array) {
for (int i = 0; i < array.length; i++) {
if (value == array[i]) {
return i;
}
}
throw new OkapiException("Unable to find integer '" + value + "' in integer array.");
}
 
/**
* Returns <code>true</code> if the specified array contains the specified value.
*
* @param array the array to check in
* @param value the value to check for
* @return true if the specified array contains the specified value
*/
public static boolean contains(final int[] array, final int value) {
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
 
/**
* Returns <code>true</code> if the specified array contains the specified sub-array at the
* specified index.
*
* @param array the array to search in
* @param searchFor the sub-array to search for
* @param index the index at which to search
* @return whether or not the specified array contains the specified sub-array at the specified
* index
*/
public static boolean containsAt(final byte[] array, final byte[] searchFor, final int index) {
for (int i = 0; i < searchFor.length; i++) {
if (index + i >= array.length || array[index + i] != searchFor[i]) {
return false;
}
}
return true;
}
 
/**
* Inserts the specified array into the specified original array at the specified index.
*
* @param original the original array into which we want to insert another array
* @param index the index at which we want to insert the array
* @param inserted the array that we want to insert
* @return the combined array
*/
public static int[] insertArray(final int[] original, final int index, final int[] inserted) {
final int[] modified = new int[original.length + inserted.length];
System.arraycopy(original, 0, modified, 0, index);
System.arraycopy(inserted, 0, modified, index, inserted.length);
System.arraycopy(original, index, modified, index + inserted.length, modified.length - index - inserted.length);
return modified;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/SymbolRenderer.java
New file
0,0 → 1,34
/*
* Copyright 2015 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import java.io.IOException;
 
import uk.org.okapibarcode.backend.Symbol;
 
/**
* Renders symbols to some output format.
*/
public interface SymbolRenderer {
 
/**
* Renders the specified symbology.
*
* @param symbol the symbology to render
* @throws IOException if there is an I/O error
*/
void render(Symbol symbol) throws IOException;
 
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/Java2DRenderer.java
New file
0,0 → 1,169
/*
* Copyright 2014-2015 Robin Stuart, Robert Elliott, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER;
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY;
 
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.Collections;
import java.util.List;
 
import uk.org.okapibarcode.backend.Hexagon;
import uk.org.okapibarcode.backend.HumanReadableAlignment;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.backend.TextBox;
 
/**
* Renders symbologies using the Java 2D API.
*/
public class Java2DRenderer implements SymbolRenderer {
 
/** The graphics to render to. */
private final Graphics2D g2d;
 
/** The magnification factor to apply. */
private final double magnification;
 
/** The paper (background) color. */
private final Color paper;
 
/** The ink (foreground) color. */
private final Color ink;
 
/**
* Creates a new Java 2D renderer. If the specified paper color is <tt>null</tt>, the symbol is
* drawn without clearing the existing <tt>g2d</tt> background.
*
* @param g2d the graphics to render to
* @param magnification the magnification factor to apply
* @param paper the paper (background) color (may be <tt>null</tt>)
* @param ink the ink (foreground) color
*/
public Java2DRenderer(final Graphics2D g2d, final double magnification, final Color paper, final Color ink) {
this.g2d = g2d;
this.magnification = magnification;
this.paper = paper;
this.ink = ink;
}
 
/** {@inheritDoc} */
@Override
public void render(final Symbol symbol) {
 
final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification);
final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification);
 
Font f = symbol.getFont();
if (f != null) {
f = f.deriveFont((float) (f.getSize2D() * this.magnification));
} else {
f = new Font(symbol.getFontName(), Font.PLAIN, (int) (symbol.getFontSize() * this.magnification));
f = f.deriveFont(Collections.singletonMap(TextAttribute.TRACKING, 0));
}
 
final Font oldFont = this.g2d.getFont();
final Color oldColor = this.g2d.getColor();
 
if (this.paper != null) {
final int w = (int) (symbol.getWidth() * this.magnification);
final int h = (int) (symbol.getHeight() * this.magnification);
this.g2d.setColor(this.paper);
this.g2d.fillRect(0, 0, w, h);
}
 
this.g2d.setColor(this.ink);
 
for (final Rectangle2D.Double rect : symbol.getRectangles()) {
final double x = rect.x * this.magnification + marginX;
final double y = rect.y * this.magnification + marginY;
final double w = rect.width * this.magnification;
final double h = rect.height * this.magnification;
this.g2d.fillRect((int) x, (int) y, (int) w, (int) h);
}
 
for (final TextBox text : symbol.getTexts()) {
final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment;
final Font font = alignment != JUSTIFY ? f : addTracking(f, text.width * this.magnification, text.text, this.g2d);
this.g2d.setFont(font);
final FontMetrics fm = this.g2d.getFontMetrics();
final Rectangle2D bounds = fm.getStringBounds(text.text, this.g2d);
final float y = (float) (text.y * this.magnification) + marginY;
float x;
switch (alignment) {
case LEFT:
case JUSTIFY:
x = (float) (this.magnification * text.x + marginX);
break;
case RIGHT:
x = (float) (this.magnification * text.x + this.magnification * text.width - bounds.getWidth() + marginX);
break;
case CENTER:
x = (float) (this.magnification * text.x + this.magnification * text.width / 2 - bounds.getWidth() / 2 + marginX);
break;
default:
throw new IllegalStateException("Unknown alignment: " + alignment);
}
this.g2d.drawString(text.text, x, y);
}
 
for (final Hexagon hexagon : symbol.getHexagons()) {
final Polygon polygon = new Polygon();
for (int j = 0; j < 6; j++) {
polygon.addPoint((int) (hexagon.pointX[j] * this.magnification + marginX), (int) (hexagon.pointY[j] * this.magnification + marginY));
}
this.g2d.fill(polygon);
}
 
final List<Ellipse2D.Double> target = symbol.getTarget();
for (int i = 0; i + 1 < target.size(); i += 2) {
final Ellipse2D.Double outer = adjust(target.get(i), this.magnification, marginX, marginY);
final Ellipse2D.Double inner = adjust(target.get(i + 1), this.magnification, marginX, marginY);
final Area area = new Area(outer);
area.subtract(new Area(inner));
this.g2d.fill(area);
}
 
this.g2d.setFont(oldFont);
this.g2d.setColor(oldColor);
}
 
private static Ellipse2D.Double adjust(final Ellipse2D.Double ellipse, final double magnification, final int marginX, final int marginY) {
final double x = ellipse.x * magnification + marginX;
final double y = ellipse.y * magnification + marginY;
final double w = ellipse.width * magnification + marginX;
final double h = ellipse.height * magnification + marginY;
return new Ellipse2D.Double(x, y, w, h);
}
 
private static Font addTracking(final Font baseFont, final double maxTextWidth, final String text, final Graphics2D g2d) {
final FontRenderContext frc = g2d.getFontRenderContext();
final double originalWidth = baseFont.getStringBounds(text, frc).getWidth();
final double extraSpace = maxTextWidth - originalWidth;
final double extraSpacePerGap = extraSpace / (text.length() - 1);
final double scaleX = baseFont.isTransformed() ? baseFont.getTransform().getScaleX() : 1;
final double tracking = extraSpacePerGap / (baseFont.getSize2D() * scaleX);
return baseFont.deriveFont(Collections.singletonMap(TextAttribute.TRACKING, tracking));
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/PostScriptRenderer.java
New file
0,0 → 1,211
/*
* Copyright 2015 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER;
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY;
import static uk.org.okapibarcode.util.Doubles.roughlyEqual;
 
import java.awt.Color;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.OutputStream;
 
import uk.org.okapibarcode.backend.Hexagon;
import uk.org.okapibarcode.backend.HumanReadableAlignment;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.backend.TextBox;
 
/**
* Renders symbologies to EPS (Encapsulated PostScript).
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class PostScriptRenderer implements SymbolRenderer {
 
/** The output stream to render to. */
private final OutputStream out;
 
/** The magnification factor to apply. */
private final double magnification;
 
/** The paper (background) color. */
private final Color paper;
 
/** The ink (foreground) color. */
private final Color ink;
 
/**
* Creates a new PostScript renderer.
*
* @param out the output stream to render to
* @param magnification the magnification factor to apply
* @param paper the paper (background) color
* @param ink the ink (foreground) color
*/
public PostScriptRenderer(final OutputStream out, final double magnification, final Color paper, final Color ink) {
this.out = out;
this.magnification = magnification;
this.paper = paper;
this.ink = ink;
}
 
/** {@inheritDoc} */
@Override
public void render(final Symbol symbol) throws IOException {
 
// All y dimensions are reversed because EPS origin (0,0) is at the bottom left, not top
// left
 
final String content = symbol.getContent();
final int width = (int) (symbol.getWidth() * this.magnification);
final int height = (int) (symbol.getHeight() * this.magnification);
final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification);
final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification);
 
String title;
if (content == null || content.isEmpty()) {
title = "OkapiBarcode Generated Symbol";
} else {
title = content;
}
 
try (ExtendedOutputStreamWriter writer = new ExtendedOutputStreamWriter(this.out, "%.2f")) {
 
// Header
writer.append("%!PS-Adobe-3.0 EPSF-3.0\n");
writer.append("%%Creator: OkapiBarcode\n");
writer.append("%%Title: ").append(title).append('\n');
writer.append("%%Pages: 0\n");
writer.append("%%BoundingBox: 0 0 ").appendInt(width).append(" ").appendInt(height).append("\n");
writer.append("%%EndComments\n");
 
// Definitions
writer.append("/TL { setlinewidth moveto lineto stroke } bind def\n");
writer.append("/TC { moveto 0 360 arc 360 0 arcn fill } bind def\n");
writer.append("/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def\n");
writer.append("/TB { 2 copy } bind def\n");
writer.append("/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def\n");
writer.append("/TE { pop pop } bind def\n");
 
// Background
writer.append("newpath\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(this.paper.getRed() / 255.0).append(" ").append(this.paper.getGreen() / 255.0).append(" ").append(this.paper.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(height).append(" 0.00 TB 0.00 ").append(width).append(" TR\n");
 
// Rectangles
for (int i = 0; i < symbol.getRectangles().size(); i++) {
final Rectangle2D.Double rect = symbol.getRectangles().get(i);
if (i == 0) {
writer.append("TE\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(rect.height * this.magnification).append(" ").append(height - (rect.y + rect.height) * this.magnification - marginY).append(" TB ")
.append(rect.x * this.magnification + marginX).append(" ").append(rect.width * this.magnification).append(" TR\n");
} else {
final Rectangle2D.Double prev = symbol.getRectangles().get(i - 1);
if (!roughlyEqual(rect.height, prev.height) || !roughlyEqual(rect.y, prev.y)) {
writer.append("TE\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(rect.height * this.magnification).append(" ").append(height - (rect.y + rect.height) * this.magnification - marginY).append(" ");
}
writer.append("TB ").append(rect.x * this.magnification + marginX).append(" ").append(rect.width * this.magnification).append(" TR\n");
}
}
 
// Text
for (int i = 0; i < symbol.getTexts().size(); i++) {
final TextBox text = symbol.getTexts().get(i);
final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment;
if (i == 0) {
writer.append("TE\n");
;
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
}
writer.append("matrix currentmatrix\n");
writer.append("/").append(symbol.getFontName()).append(" findfont\n");
writer.append(symbol.getFontSize() * this.magnification).append(" scalefont setfont\n");
final double y = height - text.y * this.magnification - marginY;
switch (alignment) {
case LEFT:
final double leftX = this.magnification * text.x + marginX;
writer.append(" 0 0 moveto ").append(leftX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
writer.append(" (").append(text.text).append(") show\n");
break;
case JUSTIFY:
final double textX = this.magnification * text.x + marginX;
final double textW = this.magnification * text.width;
writer.append(" 0 0 moveto ").append(textX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
writer.append(" (").append(text.text).append(") dup stringwidth pop ").append(textW).append(" sub neg 1 index length 1 sub div 0").append(" 3 -1 roll ashow\n");
break;
case RIGHT:
final double rightX = this.magnification * text.x + this.magnification * text.width + marginX;
writer.append(" 0 0 moveto ").append(rightX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
writer.append(" (").append(text.text).append(") stringwidth\n");
writer.append("pop\n");
writer.append("-1 mul 0 rmoveto\n");
writer.append(" (").append(text.text).append(") show\n");
break;
case CENTER:
final double centerX = this.magnification * text.x + this.magnification * text.width / 2 + marginX;
writer.append(" 0 0 moveto ").append(centerX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
writer.append(" (").append(text.text).append(") stringwidth\n");
writer.append("pop\n");
writer.append("-2 div 0 rmoveto\n");
writer.append(" (").append(text.text).append(") show\n");
break;
default:
throw new IllegalStateException("Unknown alignment: " + alignment);
}
writer.append("setmatrix\n");
}
 
// Circles
// Because MaxiCode size is fixed, this ignores magnification
for (int i = 0; i < symbol.getTarget().size(); i += 2) {
final Ellipse2D.Double ellipse1 = symbol.getTarget().get(i);
final Ellipse2D.Double ellipse2 = symbol.getTarget().get(i + 1);
if (i == 0) {
writer.append("TE\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
}
final double x1 = ellipse1.x + ellipse1.width / 2;
final double x2 = ellipse2.x + ellipse2.width / 2;
final double y1 = height - ellipse1.y - ellipse1.width / 2;
final double y2 = height - ellipse2.y - ellipse2.width / 2;
final double r1 = ellipse1.width / 2;
final double r2 = ellipse2.width / 2;
writer.append(x1 + marginX).append(" ").append(y1 - marginY).append(" ").append(r1).append(" ").append(x2 + marginX).append(" ").append(y2 - marginY).append(" ").append(r2).append(" ")
.append(x2 + r2 + marginX).append(" ").append(y2 - marginY).append(" TC\n");
}
 
// Hexagons
// Because MaxiCode size is fixed, this ignores magnification
for (int i = 0; i < symbol.getHexagons().size(); i++) {
final Hexagon hexagon = symbol.getHexagons().get(i);
for (int j = 0; j < 6; j++) {
writer.append(hexagon.pointX[j] + marginX).append(" ").append(height - hexagon.pointY[j] - marginY).append(" ");
}
writer.append(" TH\n");
}
 
// Footer
writer.append("\nshowpage\n");
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/SvgRenderer.java
New file
0,0 → 1,225
/*
* Copyright 2014-2015 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER;
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY;
 
import java.awt.Color;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
 
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
 
import org.w3c.dom.Document;
import org.w3c.dom.Text;
 
import uk.org.okapibarcode.backend.Hexagon;
import uk.org.okapibarcode.backend.HumanReadableAlignment;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.backend.TextBox;
 
/**
* Renders symbologies to SVG (Scalable Vector Graphics).
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class SvgRenderer implements SymbolRenderer {
 
/** The output stream to render to. */
private final OutputStream out;
 
/** The magnification factor to apply. */
private final double magnification;
 
/** The paper (background) color. */
private final Color paper;
 
/** The ink (foreground) color. */
private final Color ink;
 
/** Whether or not to include the XML prolog in the output. */
private final boolean xmlProlog;
 
/**
* Creates a new SVG renderer.
*
* @param out the output stream to render to
* @param magnification the magnification factor to apply
* @param paper the paper (background) color
* @param ink the ink (foreground) color
* @param xmlProlog whether or not to include the XML prolog in the output (usually {@code true}
* for standalone SVG documents, {@code false} for SVG content embedded directly in HTML
* documents)
*/
public SvgRenderer(final OutputStream out, final double magnification, final Color paper, final Color ink, final boolean xmlProlog) {
this.out = out;
this.magnification = magnification;
this.paper = paper;
this.ink = ink;
this.xmlProlog = xmlProlog;
}
 
/** {@inheritDoc} */
@Override
public void render(final Symbol symbol) throws IOException {
 
final String content = symbol.getContent();
final int width = (int) (symbol.getWidth() * this.magnification);
final int height = (int) (symbol.getHeight() * this.magnification);
final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification);
final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification);
 
String title;
if (content == null || content.isEmpty()) {
title = "OkapiBarcode Generated Symbol";
} else {
title = content;
}
 
final String fgColour = String.format("%02X", this.ink.getRed()) + String.format("%02X", this.ink.getGreen()) + String.format("%02X", this.ink.getBlue());
 
final String bgColour = String.format("%02X", this.paper.getRed()) + String.format("%02X", this.paper.getGreen()) + String.format("%02X", this.paper.getBlue());
 
try (ExtendedOutputStreamWriter writer = new ExtendedOutputStreamWriter(this.out, "%.2f")) {
 
// XML Prolog
if (this.xmlProlog) {
writer.append("<?xml version=\"1.0\" standalone=\"no\"?>\n");
writer.append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n");
writer.append(" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
}
 
// Header
writer.append("<svg width=\"").appendInt(width).append("\" height=\"").appendInt(height).append("\" version=\"1.1").append("\" xmlns=\"http://www.w3.org/2000/svg\">\n");
writer.append(" <desc>").append(clean(title)).append("</desc>\n");
writer.append(" <g id=\"barcode\" fill=\"#").append(fgColour).append("\">\n");
writer.append(" <rect x=\"0\" y=\"0\" width=\"").appendInt(width).append("\" height=\"").appendInt(height).append("\" fill=\"#").append(bgColour).append("\" />\n");
 
// Rectangles
for (int i = 0; i < symbol.getRectangles().size(); i++) {
final Rectangle2D.Double rect = symbol.getRectangles().get(i);
writer.append(" <rect x=\"").append(rect.x * this.magnification + marginX).append("\" y=\"").append(rect.y * this.magnification + marginY).append("\" width=\"")
.append(rect.width * this.magnification).append("\" height=\"").append(rect.height * this.magnification).append("\" />\n");
}
 
// Text
for (int i = 0; i < symbol.getTexts().size(); i++) {
final TextBox text = symbol.getTexts().get(i);
final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment;
double x;
String anchor;
switch (alignment) {
case LEFT:
case JUSTIFY:
x = this.magnification * text.x + marginX;
anchor = "start";
break;
case RIGHT:
x = this.magnification * text.x + this.magnification * text.width + marginX;
anchor = "end";
break;
case CENTER:
x = this.magnification * text.x + this.magnification * text.width / 2 + marginX;
anchor = "middle";
break;
default:
throw new IllegalStateException("Unknown alignment: " + alignment);
}
writer.append(" <text x=\"").append(x).append("\" y=\"").append(text.y * this.magnification + marginY).append("\" text-anchor=\"").append(anchor).append("\"\n");
if (alignment == JUSTIFY) {
writer.append(" textLength=\"").append(text.width * this.magnification).append("\" lengthAdjust=\"spacing\"\n");
}
writer.append(" font-family=\"").append(clean(symbol.getFontName())).append("\" font-size=\"").append(symbol.getFontSize() * this.magnification).append("\" fill=\"#")
.append(fgColour).append("\">\n");
writer.append(" ").append(clean(text.text)).append("\n");
writer.append(" </text>\n");
}
 
// Circles
for (int i = 0; i < symbol.getTarget().size(); i++) {
final Ellipse2D.Double ellipse = symbol.getTarget().get(i);
String color;
if ((i & 1) == 0) {
color = fgColour;
} else {
color = bgColour;
}
writer.append(" <circle cx=\"").append((ellipse.x + ellipse.width / 2) * this.magnification + marginX).append("\" cy=\"")
.append((ellipse.y + ellipse.width / 2) * this.magnification + marginY).append("\" r=\"").append(ellipse.width / 2 * this.magnification).append("\" fill=\"#").append(color)
.append("\" />\n");
}
 
// Hexagons
for (int i = 0; i < symbol.getHexagons().size(); i++) {
final Hexagon hexagon = symbol.getHexagons().get(i);
writer.append(" <path d=\"");
for (int j = 0; j < 6; j++) {
if (j == 0) {
writer.append("M ");
} else {
writer.append("L ");
}
writer.append(hexagon.pointX[j] * this.magnification + marginX).append(" ").append(hexagon.pointY[j] * this.magnification + marginY).append(" ");
}
writer.append("Z\" />\n");
}
 
// Footer
writer.append(" </g>\n");
writer.append("</svg>\n");
}
}
 
/**
* Cleans / sanitizes the specified string for inclusion in XML. A bit convoluted, but we're
* trying to do it without adding an external dependency just for this...
*
* @param s the string to be cleaned / sanitized
* @return the cleaned / sanitized string
*/
protected String clean(String s) {
 
// remove control characters
s = s.replaceAll("[\u0000-\u001f]", "");
 
// escape XML characters
try {
final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
final Text text = document.createTextNode(s);
final Transformer transformer = TransformerFactory.newInstance().newTransformer();
final DOMSource source = new DOMSource(text);
final StringWriter writer = new StringWriter();
final StreamResult result = new StreamResult(writer);
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(source, result);
return writer.toString();
} catch (ParserConfigurationException | TransformerException | TransformerFactoryConfigurationError e) {
return s;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/ExtendedOutputStreamWriter.java
New file
0,0 → 1,80
/*
* Copyright 2015 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
 
/**
* {@link OutputStreamWriter} extension which provides some convenience methods for writing numbers.
*/
class ExtendedOutputStreamWriter extends OutputStreamWriter {
 
/** Format to use when writing doubles to the stream. */
private final String doubleFormat;
 
/**
* Creates a new extended output stream writer, using the UTF-8 charset.
*
* @param out the stream to write to
* @param doubleFormat the format to use when writing doubles to the stream
*/
public ExtendedOutputStreamWriter(final OutputStream out, final String doubleFormat) {
super(out, StandardCharsets.UTF_8);
this.doubleFormat = doubleFormat;
}
 
/** {@inheritDoc} */
@Override
public ExtendedOutputStreamWriter append(final CharSequence cs) throws IOException {
super.append(cs);
return this;
}
 
/** {@inheritDoc} */
@Override
public ExtendedOutputStreamWriter append(final CharSequence cs, final int start, final int end) throws IOException {
super.append(cs, start, end);
return this;
}
 
/**
* Writes the specified double to the stream, formatted according to the format specified in the
* constructor.
*
* @param d the double to write to the stream
* @return this writer
* @throws IOException if an I/O error occurs
*/
public ExtendedOutputStreamWriter append(final double d) throws IOException {
super.append(String.format(Locale.ROOT, this.doubleFormat, d));
return this;
}
 
/**
* Writes the specified integer to the stream.
*
* @param i the integer to write to the stream
* @return this writer
* @throws IOException if an I/O error occurs
*/
public ExtendedOutputStreamWriter appendInt(final int i) throws IOException {
super.append(String.valueOf(i));
return this;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/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/AztecCode.java
New file
0,0 → 1,1824
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static java.nio.charset.StandardCharsets.US_ASCII;
import static uk.org.okapibarcode.util.Arrays.insertArray;
 
/**
* <p>
* Implements Aztec Code bar code symbology According to ISO/IEC 24778:2008.
*
* <p>
* Aztec Code can encode 8-bit ISO 8859-1 (Latin-1) data (except 0x00 Null characters) up to a
* maximum length of approximately 3800 numeric characters, 3000 alphabetic characters or 1900 bytes
* of data in a two-dimensional matrix symbol.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class AztecCode extends Symbol {
 
/* 27 x 27 data grid */
private static final int[] COMPACT_AZTEC_MAP = { 609, 608, 411, 413, 415, 417, 419, 421, 423, 425, 427, 429, 431, 433, 435, 437, 439, 441, 443, 445, 447, 449, 451, 453, 455, 457, 459, 607, 606,
410, 412, 414, 416, 418, 420, 422, 424, 426, 428, 430, 432, 434, 436, 438, 440, 442, 444, 446, 448, 450, 452, 454, 456, 458, 605, 604, 409, 408, 243, 245, 247, 249, 251, 253, 255, 257,
259, 261, 263, 265, 267, 269, 271, 273, 275, 277, 279, 281, 283, 460, 461, 603, 602, 407, 406, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276,
278, 280, 282, 462, 463, 601, 600, 405, 404, 241, 240, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 284, 285, 464, 465, 599, 598, 403, 402, 239,
238, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 286, 287, 466, 467, 597, 596, 401, 400, 237, 236, 105, 104, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21,
23, 25, 27, 140, 141, 288, 289, 468, 469, 595, 594, 399, 398, 235, 234, 103, 102, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 142, 143, 290, 291, 470, 471, 593, 592, 397, 396, 233,
232, 101, 100, 1, 1, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 0, 1, 28, 29, 144, 145, 292, 293, 472, 473, 591, 590, 395, 394, 231, 230, 99, 98, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 30, 31,
146, 147, 294, 295, 474, 475, 589, 588, 393, 392, 229, 228, 97, 96, 2027, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2007, 32, 33, 148, 149, 296, 297, 476, 477, 587, 586, 391, 390, 227, 226, 95, 94, 2026,
1, 0, 1, 1, 1, 1, 1, 0, 1, 2008, 34, 35, 150, 151, 298, 299, 478, 479, 585, 584, 389, 388, 225, 224, 93, 92, 2025, 1, 0, 1, 0, 0, 0, 1, 0, 1, 2009, 36, 37, 152, 153, 300, 301, 480, 481,
583, 582, 387, 386, 223, 222, 91, 90, 2024, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2010, 38, 39, 154, 155, 302, 303, 482, 483, 581, 580, 385, 384, 221, 220, 89, 88, 2023, 1, 0, 1, 0, 0, 0, 1, 0, 1,
2011, 40, 41, 156, 157, 304, 305, 484, 485, 579, 578, 383, 382, 219, 218, 87, 86, 2022, 1, 0, 1, 1, 1, 1, 1, 0, 1, 2012, 42, 43, 158, 159, 306, 307, 486, 487, 577, 576, 381, 380, 217, 216,
85, 84, 2021, 1, 0, 0, 0, 0, 0, 0, 0, 1, 2013, 44, 45, 160, 161, 308, 309, 488, 489, 575, 574, 379, 378, 215, 214, 83, 82, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 46, 47, 162, 163, 310, 311, 490,
491, 573, 572, 377, 376, 213, 212, 81, 80, 0, 0, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 0, 0, 48, 49, 164, 165, 312, 313, 492, 493, 571, 570, 375, 374, 211, 210, 78, 76, 74, 72, 70, 68,
66, 64, 62, 60, 58, 56, 54, 50, 51, 166, 167, 314, 315, 494, 495, 569, 568, 373, 372, 209, 208, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59, 57, 55, 52, 53, 168, 169, 316, 317, 496, 497,
567, 566, 371, 370, 206, 204, 202, 200, 198, 196, 194, 192, 190, 188, 186, 184, 182, 180, 178, 176, 174, 170, 171, 318, 319, 498, 499, 565, 564, 369, 368, 207, 205, 203, 201, 199, 197,
195, 193, 191, 189, 187, 185, 183, 181, 179, 177, 175, 172, 173, 320, 321, 500, 501, 563, 562, 366, 364, 362, 360, 358, 356, 354, 352, 350, 348, 346, 344, 342, 340, 338, 336, 334, 332,
330, 328, 326, 322, 323, 502, 503, 561, 560, 367, 365, 363, 361, 359, 357, 355, 353, 351, 349, 347, 345, 343, 341, 339, 337, 335, 333, 331, 329, 327, 324, 325, 504, 505, 558, 556, 554,
552, 550, 548, 546, 544, 542, 540, 538, 536, 534, 532, 530, 528, 526, 524, 522, 520, 518, 516, 514, 512, 510, 506, 507, 559, 557, 555, 553, 551, 549, 547, 545, 543, 541, 539, 537, 535,
533, 531, 529, 527, 525, 523, 521, 519, 517, 515, 513, 511, 508, 509 };
 
private static final int[][] AZTEC_MAP = new int[151][151];
 
/*
* From Table 2:
*
* 1 = upper 2 = lower 4 = mixed 8 = punctuation 16 = digits 32 = binary
*
* Values can be OR'ed, so e.g. 12 = 4 | 8, and 23 = 1 | 2 | 4 | 16
*/
private static final int[] AZTEC_CODE_SET = { 32, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 4, 4, 4, 4, 4, 23, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
24, 8, 24, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 4, 8, 4, 4, 4, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 4, 8, 4, 4 };
 
/* From Table 2 */
private static final int[] AZTEC_SYMBOL_CHAR = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 300, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 19, 1, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 301, 18, 302, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 21, 22, 23, 24, 25, 26, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 27, 21, 28, 22, 23, 24, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 29, 25, 30, 26, 27 };
 
/*
* Problem characters are: 300: Carriage Return (ASCII 13) 301: Comma (ASCII 44) 302: Full Stop
* (ASCII 46)
*/
private static final String[] PENTBIT = { "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000",
"10001", "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010", "11011", "11100", "11101", "11110", "11111" };
 
private static final String[] QUADBIT = { "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" };
 
private static final String[] TRIBIT = { "000", "001", "010", "011", "100", "101", "110", "111" };
 
/* Codewords per symbol */
private static final int[] AZTEC_SIZES = { 21, 48, 60, 88, 120, 156, 196, 240, 230, 272, 316, 364, 416, 470, 528, 588, 652, 720, 790, 864, 940, 1020, 920, 992, 1066, 1144, 1224, 1306, 1392, 1480,
1570, 1664 };
 
private static final int[] AZTEC_COMPACT_SIZES = { 17, 40, 51, 76 };
 
/* Data bits per symbol maximum with 10% error correction */
private static final int[] AZTEC_10_DATA_SIZES = { 96, 246, 408, 616, 840, 1104, 1392, 1704, 2040, 2420, 2820, 3250, 3720, 4200, 4730, 5270, 5840, 6450, 7080, 7750, 8430, 9150, 9900, 10680, 11484,
12324, 13188, 14076, 15000, 15948, 16920, 17940 };
 
/* Data bits per symbol maximum with 23% error correction */
private static final int[] AZTEC_23_DATA_SIZES = { 84, 204, 352, 520, 720, 944, 1184, 1456, 1750, 2070, 2410, 2780, 3180, 3590, 4040, 4500, 5000, 5520, 6060, 6630, 7210, 7830, 8472, 9132, 9816,
10536, 11280, 12036, 12828, 13644, 14472, 15348 };
 
/* Data bits per symbol maximum with 36% error correction */
private static final int[] AZTEC_36_DATA_SIZES = { 66, 168, 288, 432, 592, 776, 984, 1208, 1450, 1720, 2000, 2300, 2640, 2980, 3350, 3740, 4150, 4580, 5030, 5500, 5990, 6500, 7032, 7584, 8160,
8760, 9372, 9996, 10656, 11340, 12024, 12744 };
 
/* Data bits per symbol maximum with 50% error correction */
private static final int[] AZTEC_50_DATA_SIZES = { 48, 126, 216, 328, 456, 600, 760, 936, 1120, 1330, 1550, 1790, 2050, 2320, 2610, 2910, 3230, 3570, 3920, 4290, 4670, 5070, 5484, 5916, 6360,
6828, 7308, 7800, 8316, 8844, 9384, 9948 };
 
private static final int[] AZTEC_COMPACT_10_DATA_SIZES = { 78, 198, 336, 520 };
private static final int[] AZTEC_COMPACT_23_DATA_SIZES = { 66, 168, 288, 440 };
private static final int[] AZTEC_COMPACT_36_DATA_SIZES = { 48, 138, 232, 360 };
private static final int[] AZTEC_COMPACT_50_DATA_SIZES = { 36, 102, 176, 280 };
 
private static final int[] AZTEC_OFFSET = { 66, 64, 62, 60, 57, 55, 53, 51, 49, 47, 45, 42, 40, 38, 36, 34, 32, 30, 28, 25, 23, 21, 19, 17, 15, 13, 10, 8, 6, 4, 2, 0 };
 
private static final int[] AZTEC_COMPACT_OFFSET = { 6, 4, 2, 0 };
 
/* Initialize AZTEC_MAP */
static {
 
int layer, start, length, n, i;
int x, y;
 
for (x = 0; x < 151; x++) {
for (y = 0; y < 151; y++) {
AZTEC_MAP[x][y] = 0;
}
}
 
for (layer = 1; layer < 33; layer++) {
start = 112 * (layer - 1) + 16 * (layer - 1) * (layer - 1) + 2;
length = 28 + (layer - 1) * 4 + layer * 4;
/* Top */
i = 0;
x = 64 - (layer - 1) * 2;
y = 63 - (layer - 1) * 2;
for (n = start; n < start + length; n += 2) {
AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y)] = n;
AZTEC_MAP[avoidReferenceGrid(x + i)][avoidReferenceGrid(y - 1)] = n + 1;
i++;
}
/* Right */
i = 0;
x = 78 + (layer - 1) * 2;
y = 64 - (layer - 1) * 2;
for (n = start + length; n < start + length * 2; n += 2) {
AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y + i)] = n;
AZTEC_MAP[avoidReferenceGrid(x + 1)][avoidReferenceGrid(y + i)] = n + 1;
i++;
}
/* Bottom */
i = 0;
x = 77 + (layer - 1) * 2;
y = 78 + (layer - 1) * 2;
for (n = start + length * 2; n < start + length * 3; n += 2) {
AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y)] = n;
AZTEC_MAP[avoidReferenceGrid(x - i)][avoidReferenceGrid(y + 1)] = n + 1;
i++;
}
/* Left */
i = 0;
x = 63 - (layer - 1) * 2;
y = 77 + (layer - 1) * 2;
for (n = start + length * 3; n < start + length * 4; n += 2) {
AZTEC_MAP[avoidReferenceGrid(x)][avoidReferenceGrid(y - i)] = n;
AZTEC_MAP[avoidReferenceGrid(x - 1)][avoidReferenceGrid(y - i)] = n + 1;
i++;
}
}
 
/* Central finder pattern */
for (y = 69; y <= 81; y++) {
for (x = 69; x <= 81; x++) {
AZTEC_MAP[x][y] = 1;
}
}
for (y = 70; y <= 80; y++) {
for (x = 70; x <= 80; x++) {
AZTEC_MAP[x][y] = 0;
}
}
for (y = 71; y <= 79; y++) {
for (x = 71; x <= 79; x++) {
AZTEC_MAP[x][y] = 1;
}
}
for (y = 72; y <= 78; y++) {
for (x = 72; x <= 78; x++) {
AZTEC_MAP[x][y] = 0;
}
}
for (y = 73; y <= 77; y++) {
for (x = 73; x <= 77; x++) {
AZTEC_MAP[x][y] = 1;
}
}
for (y = 74; y <= 76; y++) {
for (x = 74; x <= 76; x++) {
AZTEC_MAP[x][y] = 0;
}
}
 
/* Guide bars */
for (y = 11; y < 151; y += 16) {
for (x = 1; x < 151; x += 2) {
AZTEC_MAP[x][y] = 1;
AZTEC_MAP[y][x] = 1;
}
}
 
/* Descriptor */
for (i = 0; i < 10; i++) { /* Top */
AZTEC_MAP[avoidReferenceGrid(66 + i)][avoidReferenceGrid(64)] = 20000 + i;
}
for (i = 0; i < 10; i++) { /* Right */
AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(66 + i)] = 20010 + i;
}
for (i = 0; i < 10; i++) { /* Bottom */
AZTEC_MAP[avoidReferenceGrid(75 - i)][avoidReferenceGrid(77)] = 20020 + i;
}
for (i = 0; i < 10; i++) { /* Left */
AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(75 - i)] = 20030 + i;
}
 
/* Orientation */
AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(64)] = 1;
AZTEC_MAP[avoidReferenceGrid(65)][avoidReferenceGrid(64)] = 1;
AZTEC_MAP[avoidReferenceGrid(64)][avoidReferenceGrid(65)] = 1;
AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(64)] = 1;
AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(65)] = 1;
AZTEC_MAP[avoidReferenceGrid(77)][avoidReferenceGrid(76)] = 1;
}
 
private static int avoidReferenceGrid(final int input) {
int output = input;
if (output > 10) {
output++;
}
if (output > 26) {
output++;
}
if (output > 42) {
output++;
}
if (output > 58) {
output++;
}
if (output > 74) {
output++;
}
if (output > 90) {
output++;
}
if (output > 106) {
output++;
}
if (output > 122) {
output++;
}
if (output > 138) {
output++;
}
return output;
}
 
private int preferredSize = 0;
private int preferredEccLevel = 2;
private String structuredAppendMessageId;
private int structuredAppendPosition = 1;
private int structuredAppendTotal = 1;
 
/**
* <p>
* Sets a preferred symbol size. This value may be ignored if data string is too large to fit in
* the specified symbol size. Values correspond to symbol sizes as shown in the following table:
*
* <table summary="Available Aztec Code symbol sizes">
* <tbody>
* <tr>
* <th>Input</th>
* <th>Symbol Size</th>
* <th>Input</th>
* <th>Symbol Size</th>
* </tr>
* <tr>
* <td>1</td>
* <td>15 x 15</td>
* <td>19</td>
* <td>79 x 79</td>
* </tr>
* <tr>
* <td>2</td>
* <td>19 x 19</td>
* <td>20</td>
* <td>83 x 83</td>
* </tr>
* <tr>
* <td>3</td>
* <td>23 x 23</td>
* <td>21</td>
* <td>87 x 87</td>
* </tr>
* <tr>
* <td>4</td>
* <td>27 x 27</td>
* <td>22</td>
* <td>91 x 91</td>
* </tr>
* <tr>
* <td>5</td>
* <td>19 x 19</td>
* <td>23</td>
* <td>95 x 95</td>
* </tr>
* <tr>
* <td>6</td>
* <td>23 x 23</td>
* <td>24</td>
* <td>101 x 101</td>
* </tr>
* <tr>
* <td>7</td>
* <td>27 x 27</td>
* <td>25</td>
* <td>105 x 105</td>
* </tr>
* <tr>
* <td>8</td>
* <td>31 x 31</td>
* <td>26</td>
* <td>109 x 109</td>
* </tr>
* <tr>
* <td>9</td>
* <td>37 x 37</td>
* <td>27</td>
* <td>113 x 113</td>
* </tr>
* <tr>
* <td>10</td>
* <td>41 x 41</td>
* <td>28</td>
* <td>117 x 117</td>
* </tr>
* <tr>
* <td>11</td>
* <td>45 x 45</td>
* <td>29</td>
* <td>121 x 121</td>
* </tr>
* <tr>
* <td>12</td>
* <td>49 x 49</td>
* <td>30</td>
* <td>125 x 125</td>
* </tr>
* <tr>
* <td>13</td>
* <td>53 x 53</td>
* <td>31</td>
* <td>131 x 131</td>
* </tr>
* <tr>
* <td>14</td>
* <td>57 x 57</td>
* <td>32</td>
* <td>135 x 135</td>
* </tr>
* <tr>
* <td>15</td>
* <td>61 x 61</td>
* <td>33</td>
* <td>139 x 139</td>
* </tr>
* <tr>
* <td>16</td>
* <td>67 x 67</td>
* <td>34</td>
* <td>143 x 143</td>
* </tr>
* <tr>
* <td>17</td>
* <td>71 x 71</td>
* <td>35</td>
* <td>147 x 147</td>
* </tr>
* <tr>
* <td>18</td>
* <td>75 x 75</td>
* <td>36</td>
* <td>151 x 151</td>
* </tr>
* </tbody>
* </table>
*
* <p>
* Note that sizes 1 to 4 are the "compact" Aztec Code symbols; sizes 5 to 36 are the
* "full-range" Aztec Code symbols.
*
* @param size an integer in the range 1 - 36
*/
public void setPreferredSize(final int size) {
if (size < 1 || size > 36) {
throw new IllegalArgumentException("Invalid size: " + size);
}
this.preferredSize = size;
}
 
/**
* Returns the preferred symbol size.
*
* @return the preferred symbol size
*/
public int getPreferredSize() {
return this.preferredSize;
}
 
/**
* Sets the preferred minimum amount of symbol space dedicated to error correction. This value
* will be ignored if a symbol size has been set by <code>setPreferredSize</code>. Valid options
* are:
*
* <table summary="Error correction options">
* <tbody>
* <tr>
* <th>Mode</th>
* <th>Error Correction Capacity</th>
* </tr>
* <tr>
* <td>1</td>
* <td>&gt; 10% + 3 codewords</td>
* </tr>
* <tr>
* <td>2</td>
* <td>&gt; 23% + 3 codewords</td>
* </tr>
* <tr>
* <td>3</td>
* <td>&gt; 36% + 3 codewords</td>
* </tr>
* <tr>
* <td>4</td>
* <td>&gt; 50% + 3 codewords</td>
* </tr>
* </tbody>
* </table>
*
* @param eccLevel an integer in the range 1 - 4
*/
public void setPreferredEccLevel(final int eccLevel) {
if (eccLevel < 1 || eccLevel > 4) {
throw new IllegalArgumentException("Invalid ECC level: " + eccLevel);
}
this.preferredEccLevel = eccLevel;
}
 
/**
* Returns the preferred error correction level.
*
* @return the preferred error correction level
*/
public int getPreferredEccLevel() {
return this.preferredEccLevel;
}
 
/**
* If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured
* format, this method sets the position of this symbol in the series. Valid values are 1
* through 26 inclusive.
*
* @param position the position of this Aztec Code symbol in the structured append series
*/
public void setStructuredAppendPosition(final int position) {
if (position < 1 || position > 26) {
throw new IllegalArgumentException("Invalid Aztec Code structured append position: " + position);
}
this.structuredAppendPosition = position;
}
 
/**
* Returns the position of this Aztec Code symbol in a series of symbols using structured
* append. If this symbol is not part of such a series, this method will return <code>1</code>.
*
* @return the position of this Aztec Code symbol in a series of symbols using structured append
*/
public int getStructuredAppendPosition() {
return this.structuredAppendPosition;
}
 
/**
* If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured
* format, this method sets the total number of symbols in the series. Valid values are 1
* through 26 inclusive. A value of 1 indicates that this symbol is not part of a structured
* append series.
*
* @param total the total number of Aztec Code symbols in the structured append series
*/
public void setStructuredAppendTotal(final int total) {
if (total < 1 || total > 26) {
throw new IllegalArgumentException("Invalid Aztec Code structured append total: " + total);
}
this.structuredAppendTotal = total;
}
 
/**
* Returns the size of the series of Aztec Code symbols using structured append that this symbol
* is part of. If this symbol is not part of a structured append series, this method will return
* <code>1</code>.
*
* @return size of the series that this symbol is part of
*/
public int getStructuredAppendTotal() {
return this.structuredAppendTotal;
}
 
/**
* If this Aztec Code symbol is part of a series of Aztec Code symbols appended in a structured
* format, this method sets the unique message ID for the series. Values may not contain spaces
* and must contain only printable ASCII characters. Message IDs are optional.
*
* @param messageId the unique message ID for the series that this symbol is part of
*/
public void setStructuredAppendMessageId(final String messageId) {
if (messageId != null && !messageId.matches("^[\\x21-\\x7F]+$")) {
throw new IllegalArgumentException("Invalid Aztec Code structured append message ID: " + messageId);
}
this.structuredAppendMessageId = messageId;
}
 
/**
* Returns the unique message ID of the series of Aztec Code symbols using structured append
* that this symbol is part of. If this symbol is not part of a structured append series, this
* method will return <code>null</code>.
*
* @return the unique message ID for the series that this symbol is part of
*/
public String getStructuredAppendMessageId() {
return this.structuredAppendMessageId;
}
 
@Override
protected boolean gs1Supported() {
return true;
}
 
@Override
protected void encode() {
 
int layers;
boolean compact;
StringBuilder adjustedString;
 
if (this.inputDataType == DataType.GS1 && this.readerInit) {
throw new OkapiException("Cannot encode in GS1 and Reader Initialisation mode at the same time");
}
 
eciProcess(); // Get ECI mode
 
/* Optional structured append (Section 8 of spec) */
/* ML + UL start flag handled later, not part of data */
if (this.structuredAppendTotal != 1) {
final StringBuilder prefix = new StringBuilder();
if (this.structuredAppendMessageId != null) {
prefix.append(' ').append(this.structuredAppendMessageId).append(' ');
}
prefix.append((char) (this.structuredAppendPosition + 64)); // 1-26 as A-Z
prefix.append((char) (this.structuredAppendTotal + 64)); // 1-26 as A-Z
final int[] prefixArray = toBytes(prefix.toString(), US_ASCII);
this.inputData = insertArray(this.inputData, 0, prefixArray);
}
 
final String binaryString = generateAztecBinary();
int dataLength = binaryString.length();
 
if (this.preferredSize == 0) {
 
/* The size of the symbol can be determined by Okapi */
 
int dataMaxSize = 0;
final int compLoop = this.readerInit ? 1 : 4;
 
do {
/* Decide what size symbol to use - the smallest that fits the data */
 
int[] dataSizes;
int[] compactDataSizes;
 
switch (this.preferredEccLevel) {
/*
* For each level of error correction work out the smallest symbol which the data
* will fit in
*/
case 1:
dataSizes = AZTEC_10_DATA_SIZES;
compactDataSizes = AZTEC_COMPACT_10_DATA_SIZES;
break;
case 2:
dataSizes = AZTEC_23_DATA_SIZES;
compactDataSizes = AZTEC_COMPACT_23_DATA_SIZES;
break;
case 3:
dataSizes = AZTEC_36_DATA_SIZES;
compactDataSizes = AZTEC_COMPACT_36_DATA_SIZES;
break;
case 4:
dataSizes = AZTEC_50_DATA_SIZES;
compactDataSizes = AZTEC_COMPACT_50_DATA_SIZES;
break;
default:
throw new OkapiException("Unrecognized ECC level: " + this.preferredEccLevel);
}
 
layers = 0;
compact = false;
 
for (int i = 32; i > 0; i--) {
if (dataLength < dataSizes[i - 1]) {
layers = i;
compact = false;
dataMaxSize = dataSizes[i - 1];
}
}
 
for (int i = compLoop; i > 0; i--) {
if (dataLength < compactDataSizes[i - 1]) {
layers = i;
compact = true;
dataMaxSize = compactDataSizes[i - 1];
}
}
 
if (layers == 0) {
/* Couldn't find a symbol which fits the data */
throw new OkapiException("Input too long (too many bits for selected ECC)");
}
 
adjustedString = adjustBinaryString(binaryString, compact, layers);
dataLength = adjustedString.length();
 
} while (dataLength > dataMaxSize);
/*
* This loop will only repeat on the rare occasions when the rule about not having all
* 1s or all 0s means that the binary string has had to be lengthened beyond the maximum
* number of bits that can be encoded in a symbol of the selected size
*/
 
} else {
 
/* The size of the symbol has been specified by the user */
 
if (this.preferredSize >= 1 && this.preferredSize <= 4) {
compact = true;
layers = this.preferredSize;
} else {
compact = false;
layers = this.preferredSize - 4;
}
 
adjustedString = adjustBinaryString(binaryString, compact, layers);
 
/* Check if the data actually fits into the selected symbol size */
final int codewordSize = getCodewordSize(layers);
final int[] sizes = compact ? AZTEC_COMPACT_SIZES : AZTEC_SIZES;
final int dataMaxSize = codewordSize * (sizes[layers - 1] - 3);
if (adjustedString.length() > dataMaxSize) {
throw new OkapiException("Data too long for specified Aztec Code symbol size");
}
}
 
if (this.readerInit && compact && layers > 1) {
throw new OkapiException("Symbol is too large for reader initialization");
}
 
if (this.readerInit && layers > 22) {
throw new OkapiException("Symbol is too large for reader initialization");
}
 
final int codewordSize = getCodewordSize(layers);
final int dataBlocks = adjustedString.length() / codewordSize;
 
int eccBlocks;
if (compact) {
eccBlocks = AZTEC_COMPACT_SIZES[layers - 1] - dataBlocks;
} else {
eccBlocks = AZTEC_SIZES[layers - 1] - dataBlocks;
}
 
infoLine("Compact Mode: " + compact);
infoLine("Layers: " + layers);
infoLine("Codeword Length: " + codewordSize + " bits");
infoLine("Data Codewords: " + dataBlocks);
infoLine("ECC Codewords: " + eccBlocks);
 
/* Add ECC data to the adjusted string */
addErrorCorrection(adjustedString, codewordSize, dataBlocks, eccBlocks);
 
/* Invert the data so that actual data is on the outside and reed-solomon on the inside */
for (int i = 0; i < adjustedString.length() / 2; i++) {
final int mirror = adjustedString.length() - i - 1;
final char c = adjustedString.charAt(i);
adjustedString.setCharAt(i, adjustedString.charAt(mirror));
adjustedString.setCharAt(mirror, c);
}
 
/* Create the descriptor / mode message */
final String descriptor = createDescriptor(compact, layers, dataBlocks);
 
/* Plot all of the data into the symbol in pre-defined spiral pattern */
if (compact) {
 
this.readable = "";
this.row_count = 27 - 2 * AZTEC_COMPACT_OFFSET[layers - 1];
this.row_height = new int[this.row_count];
this.row_height[0] = -1;
this.pattern = new String[this.row_count];
for (int y = AZTEC_COMPACT_OFFSET[layers - 1]; y < 27 - AZTEC_COMPACT_OFFSET[layers - 1]; y++) {
final StringBuilder bin = new StringBuilder(27);
for (int x = AZTEC_COMPACT_OFFSET[layers - 1]; x < 27 - AZTEC_COMPACT_OFFSET[layers - 1]; x++) {
final int j = COMPACT_AZTEC_MAP[y * 27 + x];
if (j == 0) {
bin.append('0');
}
if (j == 1) {
bin.append('1');
}
if (j >= 2) {
if (j - 2 < adjustedString.length()) {
bin.append(adjustedString.charAt(j - 2));
} else {
if (j >= 2000) {
bin.append(descriptor.charAt(j - 2000));
} else {
bin.append('0');
}
}
}
}
this.row_height[y - AZTEC_COMPACT_OFFSET[layers - 1]] = 1;
this.pattern[y - AZTEC_COMPACT_OFFSET[layers - 1]] = bin2pat(bin);
}
 
} else {
 
this.readable = "";
this.row_count = 151 - 2 * AZTEC_OFFSET[layers - 1];
this.row_height = new int[this.row_count];
this.row_height[0] = -1;
this.pattern = new String[this.row_count];
for (int y = AZTEC_OFFSET[layers - 1]; y < 151 - AZTEC_OFFSET[layers - 1]; y++) {
final StringBuilder bin = new StringBuilder(151);
for (int x = AZTEC_OFFSET[layers - 1]; x < 151 - AZTEC_OFFSET[layers - 1]; x++) {
final int j = AZTEC_MAP[x][y];
if (j == 1) {
bin.append('1');
}
if (j == 0) {
bin.append('0');
}
if (j >= 2) {
if (j - 2 < adjustedString.length()) {
bin.append(adjustedString.charAt(j - 2));
} else {
if (j >= 20000) {
bin.append(descriptor.charAt(j - 20000));
} else {
bin.append('0');
}
}
}
}
this.row_height[y - AZTEC_OFFSET[layers - 1]] = 1;
this.pattern[y - AZTEC_OFFSET[layers - 1]] = bin2pat(bin);
}
}
}
 
private String generateAztecBinary() {
 
/* Encode input data into a binary string */
int i, j, k, bytes;
int curtable, newtable, lasttable, chartype, maplength, blocks;
final int[] charmap = new int[2 * this.inputData.length];
final int[] typemap = new int[2 * this.inputData.length];
final int[] blockType = new int[this.inputData.length + 1];
final int[] blockLength = new int[this.inputData.length + 1];
 
/* Lookup input string in encoding table */
maplength = 0;
 
/* Add FNC1 to beginning of GS1 messages */
if (this.inputDataType == DataType.GS1) {
charmap[maplength] = 0; // FLG
typemap[maplength++] = 8; // PUNC
charmap[maplength] = 400; // (0)
typemap[maplength++] = 8; // PUNC
}
 
if (this.eciMode != 3) {
int flagNumber;
 
charmap[maplength] = 0; // FLG
typemap[maplength++] = 8; // PUNC
 
flagNumber = 6;
 
if (this.eciMode < 100000) {
flagNumber = 5;
}
 
if (this.eciMode < 10000) {
flagNumber = 4;
}
 
if (this.eciMode < 1000) {
flagNumber = 3;
}
 
if (this.eciMode < 100) {
flagNumber = 2;
}
 
if (this.eciMode < 10) {
flagNumber = 1;
}
 
charmap[maplength] = 400 + flagNumber;
typemap[maplength++] = 8; // PUNC
}
 
for (i = 0; i < this.inputData.length; i++) {
if (this.inputData[i] == FNC1) {
/* FNC1 represented by FLG(0) */
charmap[maplength] = 0; // FLG
typemap[maplength++] = 8; // PUNC
charmap[maplength] = 400; // (0)
typemap[maplength++] = 8; // PUNC
} else {
if (this.inputData[i] > 0x7F || this.inputData[i] == 0x00) {
charmap[maplength] = this.inputData[i];
typemap[maplength++] = 32; // BINARY
} else {
charmap[maplength] = AZTEC_SYMBOL_CHAR[this.inputData[i]];
typemap[maplength++] = AZTEC_CODE_SET[this.inputData[i]];
}
}
}
 
/* Look for double character encoding possibilities */
for (i = 0; i < maplength - 1; i++) {
if (charmap[i] == 300 && charmap[i + 1] == 11 && typemap[i] == 12 && typemap[i + 1] == 4) {
/* CR LF combination */
charmap[i] = 2;
typemap[i] = 8; // PUNC
if (i + 1 != maplength) {
for (j = i + 1; j < maplength; j++) {
charmap[j] = charmap[j + 1];
typemap[j] = typemap[j + 1];
}
}
maplength--;
}
 
if (charmap[i] == 302 && charmap[i + 1] == 1 && typemap[i] == 24 && typemap[i + 1] == 23) {
/* . SP combination */
charmap[i] = 3;
typemap[i] = 8; // PUNC;
if (i + 1 != maplength) {
for (j = i + 1; j < maplength; j++) {
charmap[j] = charmap[j + 1];
typemap[j] = typemap[j + 1];
}
}
maplength--;
}
 
if (charmap[i] == 301 && charmap[i + 1] == 1 && typemap[i] == 24 && typemap[i + 1] == 23) {
/* , SP combination */
charmap[i] = 4;
typemap[i] = 8; // PUNC;
if (i + 1 != maplength) {
for (j = i + 1; j < maplength; j++) {
charmap[j] = charmap[j + 1];
typemap[j] = typemap[j + 1];
}
}
maplength--;
}
 
if (charmap[i] == 21 && charmap[i + 1] == 1 && typemap[i] == 8 && typemap[i + 1] == 23) {
/* : SP combination */
charmap[i] = 5;
typemap[i] = 8; // PUNC;
if (i + 1 != maplength) {
for (j = i + 1; j < maplength; j++) {
charmap[j] = charmap[j + 1];
typemap[j] = typemap[j + 1];
}
}
maplength--;
}
}
 
/* look for blocks of characters which use the same table */
blocks = 0;
for (i = 0; i < maplength; i++) {
if (i > 0 && typemap[i] == typemap[i - 1]) {
blockLength[blocks - 1]++;
} else {
blocks++;
blockType[blocks - 1] = typemap[i];
blockLength[blocks - 1] = 1;
}
}
 
if ((blockType[0] & 1) != 0) {
blockType[0] = 1;
}
if ((blockType[0] & 2) != 0) {
blockType[0] = 2;
}
if ((blockType[0] & 4) != 0) {
blockType[0] = 4;
}
if ((blockType[0] & 8) != 0) {
blockType[0] = 8;
}
 
if (blocks > 1) {
 
/* look for adjacent blocks which can use the same table (left to right search) */
for (i = 1; i < blocks; i++) {
if ((blockType[i] & blockType[i - 1]) != 0) {
blockType[i] = blockType[i] & blockType[i - 1];
}
}
 
if ((blockType[blocks - 1] & 1) != 0) {
blockType[blocks - 1] = 1;
}
if ((blockType[blocks - 1] & 2) != 0) {
blockType[blocks - 1] = 2;
}
if ((blockType[blocks - 1] & 4) != 0) {
blockType[blocks - 1] = 4;
}
if ((blockType[blocks - 1] & 8) != 0) {
blockType[blocks - 1] = 8;
}
 
/* look for adjacent blocks which can use the same table (right to left search) */
for (i = blocks - 2; i > 0; i--) {
if ((blockType[i] & blockType[i + 1]) != 0) {
blockType[i] = blockType[i] & blockType[i + 1];
}
}
 
/* determine the encoding table for characters which do not fit with adjacent blocks */
for (i = 1; i < blocks; i++) {
if ((blockType[i] & 8) != 0) {
blockType[i] = 8;
}
if ((blockType[i] & 4) != 0) {
blockType[i] = 4;
}
if ((blockType[i] & 2) != 0) {
blockType[i] = 2;
}
if ((blockType[i] & 1) != 0) {
blockType[i] = 1;
}
}
 
/*
* if less than 4 characters are preceded and followed by binary blocks then it is more
* efficient to also encode these in binary
*/
 
// for (i = 1; i < blocks - 1; i++) {
// if ((blockType[i - 1] == 32) && (blockLength[i] < 4)) {
// int nonBinaryLength = blockLength[i];
// for (int l = i; ((l < blocks) && (blockType[l] != 32)); l++) {
// nonBinaryLength += blockLength[l];
// }
// if (nonBinaryLength < 4) {
// blockType[i] = 32;
// }
// }
// }
 
/* Combine blocks of the same type */
i = 0;
do {
if (blockType[i] == blockType[i + 1]) {
blockLength[i] += blockLength[i + 1];
for (j = i + 1; j < blocks - 1; j++) {
blockType[j] = blockType[j + 1];
blockLength[j] = blockLength[j + 1];
}
blocks--;
} else {
i++;
}
} while (i < blocks - 1);
}
 
/* Put the adjusted block data back into typemap */
j = 0;
for (i = 0; i < blocks; i++) {
if (blockLength[i] < 3 && blockType[i] != 32) { /* Shift character(s) needed */
 
for (k = 0; k < blockLength[i]; k++) {
typemap[j + k] = blockType[i] + 64;
}
} else { /* Latch character (or byte mode) needed */
 
for (k = 0; k < blockLength[i]; k++) {
typemap[j + k] = blockType[i];
}
}
j += blockLength[i];
}
 
/* Don't shift an initial capital letter */
if (maplength > 0 && typemap[0] == 65) {
typemap[0] = 1;
}
 
/*
* Problem characters (those that appear in different tables with different values) can now
* be resolved into their tables
*/
for (i = 0; i < maplength; i++) {
if (charmap[i] >= 300 && charmap[i] < 400) {
curtable = typemap[i];
if (curtable > 64) {
curtable -= 64;
}
switch (charmap[i]) {
case 300:
/* Carriage Return */
switch (curtable) {
case 8:
charmap[i] = 1;
break; // PUNC
case 4:
charmap[i] = 14;
break; // PUNC
}
break;
case 301:
/* Comma */
switch (curtable) {
case 8:
charmap[i] = 17;
break; // PUNC
case 16:
charmap[i] = 12;
break; // DIGIT
}
break;
case 302:
/* Full Stop */
switch (curtable) {
case 8:
charmap[i] = 19;
break; // PUNC
case 16:
charmap[i] = 13;
break; // DIGIT
}
break;
}
}
}
 
final StringBuilder binaryString = new StringBuilder();
info("Encoding: ");
curtable = 1; /* start with 1 table */
lasttable = 1;
 
/* Optional structured append start flag (Section 8 of spec) */
if (this.structuredAppendTotal != 1) {
binaryString.append(PENTBIT[29]);
info("ML ");
binaryString.append(PENTBIT[29]);
info("UL ");
}
 
for (i = 0; i < maplength; i++) {
newtable = curtable;
if (typemap[i] != curtable && charmap[i] < 400) {
/* Change table */
if (curtable == 32) {
/*
* If ending binary mode the current table is the same as when entering binary
* mode
*/
curtable = lasttable;
newtable = lasttable;
}
if (typemap[i] > 64) {
/* Shift character */
switch (typemap[i]) {
case 64 + 1:
/* To UPPER */
switch (curtable) {
case 2:
/* US */
binaryString.append(PENTBIT[28]);
info("US ");
break;
case 4:
/* UL */
binaryString.append(PENTBIT[29]);
info("UL ");
newtable = 1;
break;
case 8:
/* UL */
binaryString.append(PENTBIT[31]);
info("UL ");
newtable = 1;
break;
case 16:
/* US */
binaryString.append(QUADBIT[15]);
info("US ");
break;
}
break;
case 64 + 2:
/* To LOWER */
switch (curtable) {
case 1:
/* LL */
binaryString.append(PENTBIT[28]);
info("LL ");
newtable = 2;
break;
case 4:
/* LL */
binaryString.append(PENTBIT[28]);
info("LL ");
newtable = 2;
break;
case 8:
/* UL LL */
binaryString.append(PENTBIT[31]);
info("UL ");
binaryString.append(PENTBIT[28]);
info("LL ");
newtable = 2;
break;
case 16:
/* UL LL */
binaryString.append(QUADBIT[14]);
info("UL ");
binaryString.append(PENTBIT[28]);
info("LL ");
newtable = 2;
break;
}
break;
case 64 + 4:
/* To MIXED */
switch (curtable) {
case 1:
/* ML */
binaryString.append(PENTBIT[29]);
info("ML ");
newtable = 4;
break;
case 2:
/* ML */
binaryString.append(PENTBIT[29]);
info("ML ");
newtable = 4;
break;
case 8:
/* UL ML */
binaryString.append(PENTBIT[31]);
info("UL ");
binaryString.append(PENTBIT[29]);
info("ML ");
newtable = 4;
break;
case 16:
/* UL ML */
binaryString.append(QUADBIT[14]);
info("UL ");
binaryString.append(PENTBIT[29]);
info("ML ");
newtable = 4;
break;
}
break;
case 64 + 8:
/* To PUNC */
switch (curtable) {
case 1:
/* PS */
binaryString.append(PENTBIT[0]);
info("PS ");
break;
case 2:
/* PS */
binaryString.append(PENTBIT[0]);
info("PS ");
break;
case 4:
/* PS */
binaryString.append(PENTBIT[0]);
info("PS ");
break;
case 16:
/* PS */
binaryString.append(QUADBIT[0]);
info("PS ");
break;
}
break;
case 64 + 16:
/* To DIGIT */
switch (curtable) {
case 1:
/* DL */
binaryString.append(PENTBIT[30]);
info("DL ");
newtable = 16;
break;
case 2:
/* DL */
binaryString.append(PENTBIT[30]);
info("DL ");
newtable = 16;
break;
case 4:
/* UL DL */
binaryString.append(PENTBIT[29]);
info("UL ");
binaryString.append(PENTBIT[30]);
info("DL ");
newtable = 16;
break;
case 8:
/* UL DL */
binaryString.append(PENTBIT[31]);
info("UL ");
binaryString.append(PENTBIT[30]);
info("DL ");
newtable = 16;
break;
}
break;
}
} else {
/* Latch character */
switch (typemap[i]) {
case 1:
/* To UPPER */
switch (curtable) {
case 2:
/* ML UL */
binaryString.append(PENTBIT[29]);
info("ML ");
binaryString.append(PENTBIT[29]);
info("UL ");
newtable = 1;
break;
case 4:
/* UL */
binaryString.append(PENTBIT[29]);
info("UL ");
newtable = 1;
break;
case 8:
/* UL */
binaryString.append(PENTBIT[31]);
info("UL ");
newtable = 1;
break;
case 16:
/* UL */
binaryString.append(QUADBIT[14]);
info("UL ");
newtable = 1;
break;
}
break;
case 2:
/* To LOWER */
switch (curtable) {
case 1:
/* LL */
binaryString.append(PENTBIT[28]);
info("LL ");
newtable = 2;
break;
case 4:
/* LL */
binaryString.append(PENTBIT[28]);
info("LL ");
newtable = 2;
break;
case 8:
/* UL LL */
binaryString.append(PENTBIT[31]);
info("UL ");
binaryString.append(PENTBIT[28]);
info("LL ");
newtable = 2;
break;
case 16:
/* UL LL */
binaryString.append(QUADBIT[14]);
info("UL ");
binaryString.append(PENTBIT[28]);
info("LL ");
newtable = 2;
break;
}
break;
case 4:
/* To MIXED */
switch (curtable) {
case 1:
/* ML */
binaryString.append(PENTBIT[29]);
info("ML ");
newtable = 4;
break;
case 2:
/* ML */
binaryString.append(PENTBIT[29]);
info("ML ");
newtable = 4;
break;
case 8:
/* UL ML */
binaryString.append(PENTBIT[31]);
info("UL ");
binaryString.append(PENTBIT[29]);
info("ML ");
newtable = 4;
break;
case 16:
/* UL ML */
binaryString.append(QUADBIT[14]);
info("UL ");
binaryString.append(PENTBIT[29]);
info("ML ");
newtable = 4;
break;
}
break;
case 8:
/* To PUNC */
switch (curtable) {
case 1:
/* ML PL */
binaryString.append(PENTBIT[29]);
info("ML ");
binaryString.append(PENTBIT[30]);
info("PL ");
newtable = 8;
break;
case 2:
/* ML PL */
binaryString.append(PENTBIT[29]);
info("ML ");
binaryString.append(PENTBIT[30]);
info("PL ");
newtable = 8;
break;
case 4:
/* PL */
binaryString.append(PENTBIT[30]);
info("PL ");
newtable = 8;
break;
case 16:
/* UL ML PL */
binaryString.append(QUADBIT[14]);
info("UL ");
binaryString.append(PENTBIT[29]);
info("ML ");
binaryString.append(PENTBIT[30]);
info("PL ");
newtable = 8;
break;
}
break;
case 16:
/* To DIGIT */
switch (curtable) {
case 1:
/* DL */
binaryString.append(PENTBIT[30]);
info("DL ");
newtable = 16;
break;
case 2:
/* DL */
binaryString.append(PENTBIT[30]);
info("DL ");
newtable = 16;
break;
case 4:
/* UL DL */
binaryString.append(PENTBIT[29]);
info("UL ");
binaryString.append(PENTBIT[30]);
info("DL ");
newtable = 16;
break;
case 8:
/* UL DL */
binaryString.append(PENTBIT[31]);
info("UL ");
binaryString.append(PENTBIT[30]);
info("DL ");
newtable = 16;
break;
}
break;
case 32:
/* To BINARY */
lasttable = curtable;
switch (curtable) {
case 1:
/* BS */
binaryString.append(PENTBIT[31]);
info("BS ");
newtable = 32;
break;
case 2:
/* BS */
binaryString.append(PENTBIT[31]);
info("BS ");
newtable = 32;
break;
case 4:
/* BS */
binaryString.append(PENTBIT[31]);
info("BS ");
newtable = 32;
break;
case 8:
/* UL BS */
binaryString.append(PENTBIT[31]);
info("UL ");
binaryString.append(PENTBIT[31]);
info("BS ");
lasttable = 1;
newtable = 32;
break;
case 16:
/* UL BS */
binaryString.append(QUADBIT[14]);
info("UL ");
binaryString.append(PENTBIT[31]);
info("BS ");
lasttable = 1;
newtable = 32;
break;
}
 
bytes = 0;
do {
bytes++;
} while (typemap[i + bytes - 1] == 32);
bytes--;
 
if (bytes > 2079) {
throw new OkapiException("Input too long");
}
 
if (bytes > 31) {
/* Put 00000 followed by 11-bit number of bytes less 31 */
binaryString.append("00000");
for (int weight = 0x400; weight > 0; weight = weight >> 1) {
if ((bytes - 31 & weight) != 0) {
binaryString.append('1');
} else {
binaryString.append('0');
}
}
} else {
/* Put 5-bit number of bytes */
for (int weight = 0x10; weight > 0; weight = weight >> 1) {
if ((bytes & weight) != 0) {
binaryString.append('1');
} else {
binaryString.append('0');
}
}
}
 
break;
}
}
}
/* Add data to the binary string */
curtable = newtable;
chartype = typemap[i];
if (chartype > 64) {
chartype -= 64;
}
switch (chartype) {
case 1:
case 2:
case 4:
case 8:
if (charmap[i] >= 400) {
info("FLG(" + (charmap[i] - 400) + ") ");
binaryString.append(TRIBIT[charmap[i] - 400]);
if (charmap[i] != 400) {
/* ECI */
binaryString.append(eciToBinary());
}
} else {
binaryString.append(PENTBIT[charmap[i]]);
infoSpace(charmap[i]);
}
break;
case 16:
binaryString.append(QUADBIT[charmap[i]]);
infoSpace(charmap[i]);
break;
case 32:
for (int weight = 0x80; weight > 0; weight = weight >> 1) {
if ((charmap[i] & weight) != 0) {
binaryString.append('1');
} else {
binaryString.append('0');
}
}
infoSpace(charmap[i]);
break;
}
}
 
infoLine();
 
return binaryString.toString();
}
 
/** Adjusts bit stream so that no codewords are all 0s or all 1s, per Section 7.3.1.2 */
private StringBuilder adjustBinaryString(final String binaryString, final boolean compact, final int layers) {
 
final StringBuilder adjustedString = new StringBuilder();
final int codewordSize = getCodewordSize(layers);
int ones = 0;
 
/* Insert dummy digits needed to prevent codewords of all 0s or all 1s */
for (int i = 0; i < binaryString.length(); i++) {
if ((adjustedString.length() + 1) % codewordSize == 0) {
if (ones == codewordSize - 1) {
// codeword of B-1 1s, add dummy 0
adjustedString.append('0');
i--;
} else if (ones == 0) {
// codeword of B-1 0s, add dummy 1
adjustedString.append('1');
i--;
} else {
// no dummy value needed
adjustedString.append(binaryString.charAt(i));
}
ones = 0;
} else {
adjustedString.append(binaryString.charAt(i));
if (binaryString.charAt(i) == '1') {
ones++;
}
}
}
 
/* Add padding */
int adjustedLength = adjustedString.length();
final int remainder = adjustedLength % codewordSize;
int padBits = codewordSize - remainder;
if (padBits == codewordSize) {
padBits = 0;
}
for (int i = 0; i < padBits; i++) {
adjustedString.append('1');
}
adjustedLength = adjustedString.length();
 
/* Make sure padding didn't create an invalid (all 1s) codeword */
ones = 0;
for (int i = adjustedLength - codewordSize; i < adjustedLength && i >= 0; i++) {
if (adjustedString.charAt(i) == '1') {
ones++;
}
}
if (ones == codewordSize) {
adjustedString.setCharAt(adjustedLength - 1, '0');
}
 
/* Log the codewords */
info("Codewords: ");
for (int i = 0; i < adjustedLength / codewordSize; i++) {
int l = 0, m = 1 << codewordSize - 1;
for (int j = 0; j < codewordSize; j++) {
if (adjustedString.charAt(i * codewordSize + j) == '1') {
l += m;
}
m = m >> 1;
}
infoSpace(l);
}
infoLine();
 
/* Return the adjusted bit string */
return adjustedString;
}
 
private String eciToBinary() {
final String eciNumber = Integer.toString(this.eciMode);
final StringBuilder binary = new StringBuilder(4 * eciNumber.length());
for (int i = 0; i < eciNumber.length(); i++) {
binary.append(QUADBIT[eciNumber.charAt(i) - '0' + 2]);
infoSpace(eciNumber.charAt(i));
}
return binary.toString();
}
 
/** Creates the descriptor / mode message, per Section 7.2 */
private String createDescriptor(final boolean compact, final int layers, final int dataBlocks) {
 
final StringBuilder descriptor = new StringBuilder();
int descDataSize;
 
if (compact) {
/* The first 2 bits represent the number of layers minus 1 */
if ((layers - 1 & 0x02) != 0) {
descriptor.append('1');
} else {
descriptor.append('0');
}
if ((layers - 1 & 0x01) != 0) {
descriptor.append('1');
} else {
descriptor.append('0');
}
/* The next 6 bits represent the number of data blocks minus 1 */
if (this.readerInit) {
descriptor.append('1');
} else {
if ((dataBlocks - 1 & 0x20) != 0) {
descriptor.append('1');
} else {
descriptor.append('0');
}
}
for (int i = 0x10; i > 0; i = i >> 1) {
if ((dataBlocks - 1 & i) != 0) {
descriptor.append('1');
} else {
descriptor.append('0');
}
}
descDataSize = 2;
} else {
/* The first 5 bits represent the number of layers minus 1 */
for (int i = 0x10; i > 0; i = i >> 1) {
if ((layers - 1 & i) != 0) {
descriptor.append('1');
} else {
descriptor.append('0');
}
}
 
/* The next 11 bits represent the number of data blocks minus 1 */
if (this.readerInit) {
descriptor.append('1');
} else {
if ((dataBlocks - 1 & 0x400) != 0) {
descriptor.append('1');
} else {
descriptor.append('0');
}
}
for (int i = 0x200; i > 0; i = i >> 1) {
if ((dataBlocks - 1 & i) != 0) {
descriptor.append('1');
} else {
descriptor.append('0');
}
}
descDataSize = 4;
}
 
infoLine("Mode Message: " + descriptor);
 
/* Split into 4-bit codewords */
final int[] desc_data = new int[descDataSize];
for (int i = 0; i < descDataSize; i++) {
for (int weight = 0; weight < 4; weight++) {
if (descriptor.charAt(i * 4 + weight) == '1') {
desc_data[i] += 8 >> weight;
}
}
}
 
/*
* Add Reed-Solomon error correction with Galois Field GF(16) and prime modulus x^4 + x + 1
* (Section 7.2.3)
*/
final ReedSolomon rs = new ReedSolomon();
rs.init_gf(0x13);
if (compact) {
rs.init_code(5, 1);
rs.encode(2, desc_data);
final int[] desc_ecc = new int[6];
for (int i = 0; i < 5; i++) {
desc_ecc[i] = rs.getResult(i);
}
for (int i = 0; i < 5; i++) {
for (int weight = 0x08; weight > 0; weight = weight >> 1) {
if ((desc_ecc[4 - i] & weight) != 0) {
descriptor.append('1');
} else {
descriptor.append('0');
}
}
}
} else {
rs.init_code(6, 1);
rs.encode(4, desc_data);
final int[] desc_ecc = new int[6];
for (int i = 0; i < 6; i++) {
desc_ecc[i] = rs.getResult(i);
}
for (int i = 0; i < 6; i++) {
for (int weight = 0x08; weight > 0; weight = weight >> 1) {
if ((desc_ecc[5 - i] & weight) != 0) {
descriptor.append('1');
} else {
descriptor.append('0');
}
}
}
}
 
return descriptor.toString();
}
 
/**
* Adds error correction data to the specified binary string, which already contains the primary
* data
*/
private void addErrorCorrection(final StringBuilder adjustedString, final int codewordSize, final int dataBlocks, final int eccBlocks) {
 
int x, poly, startWeight;
 
/* Split into codewords and calculate Reed-Solomon error correction codes */
switch (codewordSize) {
case 6:
x = 32;
poly = 0x43;
startWeight = 0x20;
break;
case 8:
x = 128;
poly = 0x12d;
startWeight = 0x80;
break;
case 10:
x = 512;
poly = 0x409;
startWeight = 0x200;
break;
case 12:
x = 2048;
poly = 0x1069;
startWeight = 0x800;
break;
default:
throw new OkapiException("Unrecognized codeword size: " + codewordSize);
}
 
final ReedSolomon rs = new ReedSolomon();
final int[] data = new int[dataBlocks + 3];
final int[] ecc = new int[eccBlocks + 3];
 
for (int i = 0; i < dataBlocks; i++) {
for (int weight = 0; weight < codewordSize; weight++) {
if (adjustedString.charAt(i * codewordSize + weight) == '1') {
data[i] += x >> weight;
}
}
}
 
rs.init_gf(poly);
rs.init_code(eccBlocks, 1);
rs.encode(dataBlocks, data);
 
for (int i = 0; i < eccBlocks; i++) {
ecc[i] = rs.getResult(i);
}
 
for (int i = eccBlocks - 1; i >= 0; i--) {
for (int weight = startWeight; weight > 0; weight = weight >> 1) {
if ((ecc[i] & weight) != 0) {
adjustedString.append('1');
} else {
adjustedString.append('0');
}
}
}
}
 
/** Determines codeword bit length - Table 3 */
private static int getCodewordSize(final int layers) {
if (layers >= 23) {
return 12;
} else if (layers >= 9 && layers <= 22) {
return 10;
} else if (layers >= 3 && layers <= 8) {
return 8;
} else {
assert layers <= 2;
return 6;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/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/Code11.java
New file
0,0 → 1,215
/*
* Copyright 2014 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
/**
* <p>
* Implements Code 11 bar code symbology.
*
* <p>
* Code 11 can encode any length string consisting of the digits 0-9 and the dash character (-). One
* or two modulo-11 check digits are calculated.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class Code11 extends Symbol {
 
private static final String[] CODE_11_TABLE = { "111121", "211121", "121121", "221111", "112121", "212111", "122111", "111221", "211211", "211111", "112111" };
 
private static final char[] CHARACTER_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' };
 
/** Ratio of wide bar width to narrow bar width. */
private double moduleWidthRatio = 2;
 
/** The number of check digits to calculate ({@code 1} or {@code 2}). */
private int checkDigitCount = 2;
 
/** Optional start delimiter to be shown in the human-readable text. */
private Character startDelimiter;
 
/** Optional stop delimiter to be shown in the human-readable text. */
private Character stopDelimiter;
 
/**
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually between
* {@code 2} and {@code 3}. The default value is {@code 2}.
*
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width
*/
public void setModuleWidthRatio(final double moduleWidthRatio) {
this.moduleWidthRatio = moduleWidthRatio;
}
 
/**
* Returns the ratio of wide bar width to narrow bar width.
*
* @return the ratio of wide bar width to narrow bar width
*/
public double getModuleWidthRatio() {
return this.moduleWidthRatio;
}
 
/**
* Sets the number of check digits to calculate ({@code 1} or {@code 2}). The default value is
* {@code 2}.
*
* @param checkDigitCount the number of check digits to calculate
*/
public void setCheckDigitCount(final int checkDigitCount) {
if (checkDigitCount < 1 || checkDigitCount > 2) {
throw new IllegalArgumentException("Check digit count must be 1 or 2.");
}
this.checkDigitCount = checkDigitCount;
}
 
/**
* Returns the number of check digits to calculate (1 or 2).
*
* @return the number of check digits to calculate
*/
public int getCheckDigitCount() {
return this.checkDigitCount;
}
 
/**
* Sets an optional start delimiter to be shown in the human-readable text (defaults to
* <code>null</code>).
*
* @param startDelimiter an optional start delimiter to be shown in the human-readable text
*/
public void setStartDelimiter(final Character startDelimiter) {
this.startDelimiter = startDelimiter;
}
 
/**
* Returns the optional start delimiter to be shown in the human-readable text.
*
* @return the optional start delimiter to be shown in the human-readable text
*/
public Character getStartDelimiter() {
return this.startDelimiter;
}
 
/**
* Sets an optional stop delimiter to be shown in the human-readable text (defaults to
* <code>null</code>).
*
* @param stopDelimiter an optional stop delimiter to be shown in the human-readable text
*/
public void setStopDelimiter(final Character stopDelimiter) {
this.stopDelimiter = stopDelimiter;
}
 
/**
* Returns the optional stop delimiter to be shown in the human-readable text.
*
* @return the optional stop delimiter to be shown in the human-readable text
*/
public Character getStopDelimiter() {
return this.stopDelimiter;
}
 
/** {@inheritDoc} */
@Override
protected void encode() {
 
if (!this.content.matches("[0-9-]+")) {
throw new OkapiException("Invalid characters in input");
}
 
String horizontalSpacing = "112211";
String humanReadable = this.content;
final int length = this.content.length();
final int[] weight = new int[length + 1];
 
for (int i = 0; i < length; i++) {
final char c = this.content.charAt(i);
weight[i] = positionOf(c, CHARACTER_SET);
horizontalSpacing += CODE_11_TABLE[weight[i]];
}
 
final int checkDigitC = getCheckDigitC(weight, length);
horizontalSpacing += CODE_11_TABLE[checkDigitC];
humanReadable += CHARACTER_SET[checkDigitC];
infoLine("Check Digit C: " + checkDigitC);
 
if (this.checkDigitCount == 2) {
weight[length] = checkDigitC;
final int checkDigitK = getCheckDigitK(weight, length + 1);
horizontalSpacing += CODE_11_TABLE[checkDigitK];
humanReadable += CHARACTER_SET[checkDigitK];
infoLine("Check Digit K: " + checkDigitK);
}
 
horizontalSpacing += "112211";
 
this.readable = humanReadable;
if (this.startDelimiter != null) {
this.readable = this.startDelimiter + this.readable;
}
if (this.stopDelimiter != null) {
this.readable = this.readable + this.stopDelimiter;
}
 
this.pattern = new String[] { horizontalSpacing };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private static int getCheckDigitC(final int[] weight, final int length) {
int countC = 0;
int weightC = 1;
for (int i = length - 1; i >= 0; i--) {
countC += weightC * weight[i];
weightC++;
if (weightC > 10) {
weightC = 1;
}
}
return countC % 11;
}
 
private static int getCheckDigitK(final int[] weight, final int length) {
int countK = 0;
int weightK = 1;
for (int i = length - 1; i >= 0; i--) {
countK += weightK * weight[i];
weightK++;
if (weightK > 9) {
weightK = 1;
}
}
return countK % 11;
}
 
/** {@inheritDoc} */
@Override
protected double getModuleWidth(final int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return this.moduleWidthRatio;
}
}
 
/** {@inheritDoc} */
@Override
protected int[] getCodewords() {
return getPatternAsCodewords(6);
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/CodeOne.java
New file
0,0 → 1,1869
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
 
/**
* <p>
* Implements Code One.
*
* <p>
* Code One is able to encode the ISO 8859-1 (Latin-1) character set or GS1 data. There are two
* types of Code One symbol: variable height symbols which are roughly square (versions A thought to
* H) and fixed-height versions (version S and T). Version S symbols can only encode numeric data.
* The width of version S and version T symbols is determined by the length of the input data.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class CodeOne extends Symbol {
 
public enum Version {
NONE, A, B, C, D, E, F, G, H, S, T
}
 
private static final int[] C40_SHIFT = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 };
 
private static final int[] C40_VALUE = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22,
23, 24, 25, 26, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
 
private static final int[] TEXT_SHIFT = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3 };
 
private static final int[] TEXT_VALUE = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 22, 23, 24, 25,
26, 0, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31 };
 
private static final int[] C1_HEIGHT = { 16, 22, 28, 40, 52, 70, 104, 148 };
private static final int[] C1_WIDTH = { 18, 22, 32, 42, 54, 76, 98, 134 };
private static final int[] C1_DATA_LENGTH = { 10, 19, 44, 91, 182, 370, 732, 1480 };
private static final int[] C1_ECC_LENGTH = { 10, 16, 26, 44, 70, 140, 280, 560 };
private static final int[] C1_BLOCKS = { 1, 1, 1, 1, 1, 2, 4, 8 };
private static final int[] C1_DATA_BLOCKS = { 10, 19, 44, 91, 182, 185, 183, 185 };
private static final int[] C1_ECC_BLOCKS = { 10, 16, 26, 44, 70, 70, 70, 70 };
private static final int[] C1_GRID_WIDTH = { 4, 5, 7, 9, 12, 17, 22, 30 };
private static final int[] C1_GRID_HEIGHT = { 5, 7, 10, 15, 21, 30, 46, 68 };
 
private enum Mode {
C1_ASCII, C1_C40, C1_DECIMAL, C1_TEXT, C1_EDI, C1_BYTE
}
 
private Version preferredVersion = Version.NONE;
 
private final int[] data = new int[1500];
private final int[][] datagrid = new int[136][120];
private final boolean[][] outputGrid = new boolean[148][134];
 
/**
* Sets the preferred symbol size / version. Versions A to H are square symbols. Version S and T
* are fixed height symbols. This value may be ignored if the input data does not fit in the
* specified version.
*
* @param version the preferred symbol version
*/
public void setPreferredVersion(final Version version) {
this.preferredVersion = version;
}
 
/**
* Returns the preferred symbol version.
*
* @return the preferred symbol version
*/
public Version getPreferredVersion() {
return this.preferredVersion;
}
 
@Override
protected boolean gs1Supported() {
return true;
}
 
@Override
protected void encode() {
 
int size = 1, i, j, data_blocks;
int row, col;
int sub_version = 0;
int codewords;
final int[] ecc = new int[600];
final int[] stream = new int[2100];
int block_width;
final int length = this.content.length();
final ReedSolomon rs = new ReedSolomon();
int data_length;
int data_cw, ecc_cw;
final int[] sub_data = new int[190];
final StringBuilder bin = new StringBuilder();
 
if (!this.content.matches("[\u0000-\u00FF]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
if (this.preferredVersion == Version.S) {
/* Version S */
 
infoLine("Version: S");
 
if (length > 18) {
throw new OkapiException("Input data too long");
}
 
if (!this.content.matches("[0-9]+?")) {
throw new OkapiException("Invalid characters in input");
}
 
sub_version = 3;
codewords = 12;
block_width = 6; /* Version S-30 */
if (length <= 12) {
sub_version = 2;
codewords = 8;
block_width = 4;
} /* Version S-20 */
if (length <= 6) {
sub_version = 1;
codewords = 4;
block_width = 2;
} /* Version S-10 */
 
final BigInteger elreg = new BigInteger(this.content);
 
for (i = 0; i < codewords; i++) {
BigInteger codewordValue = elreg.shiftRight(5 * i);
codewordValue = codewordValue.and(BigInteger.valueOf(0b11111));
this.data[codewords - i - 1] = codewordValue.intValue();
}
 
logCodewords(codewords);
 
rs.init_gf(0x25);
rs.init_code(codewords, 1);
rs.encode(codewords, this.data);
 
infoLine("ECC Codeword Count: " + codewords);
 
for (i = 0; i < codewords; i++) {
stream[i] = this.data[i];
stream[i + codewords] = rs.getResult(codewords - i - 1);
}
 
for (i = 0; i < 136; i++) {
for (j = 0; j < 120; j++) {
this.datagrid[i][j] = '0';
}
}
 
i = 0;
for (row = 0; row < 2; row++) {
for (col = 0; col < block_width; col++) {
if ((stream[i] & 0x10) != 0) {
this.datagrid[row * 2][col * 5] = '1';
}
if ((stream[i] & 0x08) != 0) {
this.datagrid[row * 2][col * 5 + 1] = '1';
}
if ((stream[i] & 0x04) != 0) {
this.datagrid[row * 2][col * 5 + 2] = '1';
}
if ((stream[i] & 0x02) != 0) {
this.datagrid[row * 2 + 1][col * 5] = '1';
}
if ((stream[i] & 0x01) != 0) {
this.datagrid[row * 2 + 1][col * 5 + 1] = '1';
}
if ((stream[i + 1] & 0x10) != 0) {
this.datagrid[row * 2][col * 5 + 3] = '1';
}
if ((stream[i + 1] & 0x08) != 0) {
this.datagrid[row * 2][col * 5 + 4] = '1';
}
if ((stream[i + 1] & 0x04) != 0) {
this.datagrid[row * 2 + 1][col * 5 + 2] = '1';
}
if ((stream[i + 1] & 0x02) != 0) {
this.datagrid[row * 2 + 1][col * 5 + 3] = '1';
}
if ((stream[i + 1] & 0x01) != 0) {
this.datagrid[row * 2 + 1][col * 5 + 4] = '1';
}
i += 2;
}
}
 
infoLine("Grid Size: " + block_width + " X " + 2);
 
size = 9;
this.row_count = 8;
this.symbol_width = 10 * sub_version + 1;
}
 
if (this.preferredVersion == Version.T) {
/* Version T */
 
infoLine("Version: T");
 
for (i = 0; i < 40; i++) {
this.data[i] = 0;
}
data_length = encodeAsCode1Data();
 
if (data_length > 38) {
throw new OkapiException("Input data too long");
}
 
size = 10;
sub_version = 3;
data_cw = 38;
ecc_cw = 22;
block_width = 12;
if (data_length <= 24) {
sub_version = 2;
data_cw = 24;
ecc_cw = 16;
block_width = 8;
}
if (data_length <= 10) {
sub_version = 1;
data_cw = 10;
ecc_cw = 10;
block_width = 4;
}
 
logCodewords(data_length);
 
for (i = data_length; i < data_cw; i++) {
this.data[i] = 129; /* Pad */
}
 
/* Calculate error correction data */
rs.init_gf(0x12d);
rs.init_code(ecc_cw, 1);
rs.encode(data_cw, this.data);
 
infoLine("ECC Codeword Count: " + ecc_cw);
 
/* "Stream" combines data and error correction data */
for (i = 0; i < data_cw; i++) {
stream[i] = this.data[i];
}
for (i = 0; i < ecc_cw; i++) {
stream[data_cw + i] = rs.getResult(ecc_cw - i - 1);
}
 
for (i = 0; i < 136; i++) {
for (j = 0; j < 120; j++) {
this.datagrid[i][j] = '0';
}
}
 
i = 0;
for (row = 0; row < 5; row++) {
for (col = 0; col < block_width; col++) {
if ((stream[i] & 0x80) != 0) {
this.datagrid[row * 2][col * 4] = '1';
}
if ((stream[i] & 0x40) != 0) {
this.datagrid[row * 2][col * 4 + 1] = '1';
}
if ((stream[i] & 0x20) != 0) {
this.datagrid[row * 2][col * 4 + 2] = '1';
}
if ((stream[i] & 0x10) != 0) {
this.datagrid[row * 2][col * 4 + 3] = '1';
}
if ((stream[i] & 0x08) != 0) {
this.datagrid[row * 2 + 1][col * 4] = '1';
}
if ((stream[i] & 0x04) != 0) {
this.datagrid[row * 2 + 1][col * 4 + 1] = '1';
}
if ((stream[i] & 0x02) != 0) {
this.datagrid[row * 2 + 1][col * 4 + 2] = '1';
}
if ((stream[i] & 0x01) != 0) {
this.datagrid[row * 2 + 1][col * 4 + 3] = '1';
}
i++;
}
}
 
infoLine("Grid Size: " + block_width + " X " + 5);
 
this.row_count = 16;
this.symbol_width = sub_version * 16 + 1;
}
 
if (this.preferredVersion != Version.S && this.preferredVersion != Version.T) {
/* Version A to H */
for (i = 0; i < 1500; i++) {
this.data[i] = 0;
}
data_length = encodeAsCode1Data();
 
for (i = 7; i >= 0; i--) {
if (C1_DATA_LENGTH[i] >= data_length) {
size = i + 1;
}
}
 
if (getSize(this.preferredVersion) > size) {
size = getSize(this.preferredVersion);
}
 
final char version = (char) (size - 1 + 'A');
infoLine("Version: " + version);
logCodewords(data_length);
 
for (i = data_length; i < C1_DATA_LENGTH[size - 1]; i++) {
this.data[i] = 129; /* Pad */
}
 
/* Calculate error correction data */
data_length = C1_DATA_LENGTH[size - 1];
 
data_blocks = C1_BLOCKS[size - 1];
 
rs.init_gf(0x12d);
rs.init_code(C1_ECC_BLOCKS[size - 1], 0);
for (i = 0; i < data_blocks; i++) {
for (j = 0; j < C1_DATA_BLOCKS[size - 1]; j++) {
sub_data[j] = this.data[j * data_blocks + i];
}
rs.encode(C1_DATA_BLOCKS[size - 1], sub_data);
for (j = 0; j < C1_ECC_BLOCKS[size - 1]; j++) {
ecc[C1_ECC_LENGTH[size - 1] - (j * data_blocks + i) - 1] = rs.getResult(j);
}
}
 
infoLine("ECC Codeword Count: " + C1_ECC_LENGTH[size - 1]);
 
/* "Stream" combines data and error correction data */
for (i = 0; i < data_length; i++) {
stream[i] = this.data[i];
}
for (i = 0; i < C1_ECC_LENGTH[size - 1]; i++) {
stream[data_length + i] = ecc[i];
}
 
for (i = 0; i < 136; i++) {
for (j = 0; j < 120; j++) {
this.datagrid[i][j] = '0';
}
}
 
i = 0;
for (row = 0; row < C1_GRID_HEIGHT[size - 1]; row++) {
for (col = 0; col < C1_GRID_WIDTH[size - 1]; col++) {
if ((stream[i] & 0x80) != 0) {
this.datagrid[row * 2][col * 4] = '1';
}
if ((stream[i] & 0x40) != 0) {
this.datagrid[row * 2][col * 4 + 1] = '1';
}
if ((stream[i] & 0x20) != 0) {
this.datagrid[row * 2][col * 4 + 2] = '1';
}
if ((stream[i] & 0x10) != 0) {
this.datagrid[row * 2][col * 4 + 3] = '1';
}
if ((stream[i] & 0x08) != 0) {
this.datagrid[row * 2 + 1][col * 4] = '1';
}
if ((stream[i] & 0x04) != 0) {
this.datagrid[row * 2 + 1][col * 4 + 1] = '1';
}
if ((stream[i] & 0x02) != 0) {
this.datagrid[row * 2 + 1][col * 4 + 2] = '1';
}
if ((stream[i] & 0x01) != 0) {
this.datagrid[row * 2 + 1][col * 4 + 3] = '1';
}
i++;
}
}
 
infoLine("Grid Size: " + C1_GRID_WIDTH[size - 1] + " X " + C1_GRID_HEIGHT[size - 1]);
 
this.row_count = C1_HEIGHT[size - 1];
this.symbol_width = C1_WIDTH[size - 1];
}
 
for (i = 0; i < 148; i++) {
for (j = 0; j < 134; j++) {
this.outputGrid[i][j] = false;
}
}
 
switch (size) {
case 1:
/* Version A */
plotCentralFinder(6, 3, 1);
plotVerticalBar(4, 6, 1);
plotVerticalBar(12, 5, 0);
setGridModule(5, 12);
plotSpigot(0);
plotSpigot(15);
plotDataBlock(0, 0, 5, 4, 0, 0);
plotDataBlock(0, 4, 5, 12, 0, 2);
plotDataBlock(5, 0, 5, 12, 6, 0);
plotDataBlock(5, 12, 5, 4, 6, 2);
break;
case 2:
/* Version B */
plotCentralFinder(8, 4, 1);
plotVerticalBar(4, 8, 1);
plotVerticalBar(16, 7, 0);
setGridModule(7, 16);
plotSpigot(0);
plotSpigot(21);
plotDataBlock(0, 0, 7, 4, 0, 0);
plotDataBlock(0, 4, 7, 16, 0, 2);
plotDataBlock(7, 0, 7, 16, 8, 0);
plotDataBlock(7, 16, 7, 4, 8, 2);
break;
case 3:
/* Version C */
plotCentralFinder(11, 4, 2);
plotVerticalBar(4, 11, 1);
plotVerticalBar(26, 13, 1);
plotVerticalBar(4, 10, 0);
plotVerticalBar(26, 10, 0);
plotSpigot(0);
plotSpigot(27);
plotDataBlock(0, 0, 10, 4, 0, 0);
plotDataBlock(0, 4, 10, 20, 0, 2);
plotDataBlock(0, 24, 10, 4, 0, 4);
plotDataBlock(10, 0, 10, 4, 8, 0);
plotDataBlock(10, 4, 10, 20, 8, 2);
plotDataBlock(10, 24, 10, 4, 8, 4);
break;
case 4:
/* Version D */
plotCentralFinder(16, 5, 1);
plotVerticalBar(4, 16, 1);
plotVerticalBar(20, 16, 1);
plotVerticalBar(36, 16, 1);
plotVerticalBar(4, 15, 0);
plotVerticalBar(20, 15, 0);
plotVerticalBar(36, 15, 0);
plotSpigot(0);
plotSpigot(12);
plotSpigot(27);
plotSpigot(39);
plotDataBlock(0, 0, 15, 4, 0, 0);
plotDataBlock(0, 4, 15, 14, 0, 2);
plotDataBlock(0, 18, 15, 14, 0, 4);
plotDataBlock(0, 32, 15, 4, 0, 6);
plotDataBlock(15, 0, 15, 4, 10, 0);
plotDataBlock(15, 4, 15, 14, 10, 2);
plotDataBlock(15, 18, 15, 14, 10, 4);
plotDataBlock(15, 32, 15, 4, 10, 6);
break;
case 5:
/* Version E */
plotCentralFinder(22, 5, 2);
plotVerticalBar(4, 22, 1);
plotVerticalBar(26, 24, 1);
plotVerticalBar(48, 22, 1);
plotVerticalBar(4, 21, 0);
plotVerticalBar(26, 21, 0);
plotVerticalBar(48, 21, 0);
plotSpigot(0);
plotSpigot(12);
plotSpigot(39);
plotSpigot(51);
plotDataBlock(0, 0, 21, 4, 0, 0);
plotDataBlock(0, 4, 21, 20, 0, 2);
plotDataBlock(0, 24, 21, 20, 0, 4);
plotDataBlock(0, 44, 21, 4, 0, 6);
plotDataBlock(21, 0, 21, 4, 10, 0);
plotDataBlock(21, 4, 21, 20, 10, 2);
plotDataBlock(21, 24, 21, 20, 10, 4);
plotDataBlock(21, 44, 21, 4, 10, 6);
break;
case 6:
/* Version F */
plotCentralFinder(31, 5, 3);
plotVerticalBar(4, 31, 1);
plotVerticalBar(26, 35, 1);
plotVerticalBar(48, 31, 1);
plotVerticalBar(70, 35, 1);
plotVerticalBar(4, 30, 0);
plotVerticalBar(26, 30, 0);
plotVerticalBar(48, 30, 0);
plotVerticalBar(70, 30, 0);
plotSpigot(0);
plotSpigot(12);
plotSpigot(24);
plotSpigot(45);
plotSpigot(57);
plotSpigot(69);
plotDataBlock(0, 0, 30, 4, 0, 0);
plotDataBlock(0, 4, 30, 20, 0, 2);
plotDataBlock(0, 24, 30, 20, 0, 4);
plotDataBlock(0, 44, 30, 20, 0, 6);
plotDataBlock(0, 64, 30, 4, 0, 8);
plotDataBlock(30, 0, 30, 4, 10, 0);
plotDataBlock(30, 4, 30, 20, 10, 2);
plotDataBlock(30, 24, 30, 20, 10, 4);
plotDataBlock(30, 44, 30, 20, 10, 6);
plotDataBlock(30, 64, 30, 4, 10, 8);
break;
case 7:
/* Version G */
plotCentralFinder(47, 6, 2);
plotVerticalBar(6, 47, 1);
plotVerticalBar(27, 49, 1);
plotVerticalBar(48, 47, 1);
plotVerticalBar(69, 49, 1);
plotVerticalBar(90, 47, 1);
plotVerticalBar(6, 46, 0);
plotVerticalBar(27, 46, 0);
plotVerticalBar(48, 46, 0);
plotVerticalBar(69, 46, 0);
plotVerticalBar(90, 46, 0);
plotSpigot(0);
plotSpigot(12);
plotSpigot(24);
plotSpigot(36);
plotSpigot(67);
plotSpigot(79);
plotSpigot(91);
plotSpigot(103);
plotDataBlock(0, 0, 46, 6, 0, 0);
plotDataBlock(0, 6, 46, 19, 0, 2);
plotDataBlock(0, 25, 46, 19, 0, 4);
plotDataBlock(0, 44, 46, 19, 0, 6);
plotDataBlock(0, 63, 46, 19, 0, 8);
plotDataBlock(0, 82, 46, 6, 0, 10);
plotDataBlock(46, 0, 46, 6, 12, 0);
plotDataBlock(46, 6, 46, 19, 12, 2);
plotDataBlock(46, 25, 46, 19, 12, 4);
plotDataBlock(46, 44, 46, 19, 12, 6);
plotDataBlock(46, 63, 46, 19, 12, 8);
plotDataBlock(46, 82, 46, 6, 12, 10);
break;
case 8:
/* Version H */
plotCentralFinder(69, 6, 3);
plotVerticalBar(6, 69, 1);
plotVerticalBar(26, 73, 1);
plotVerticalBar(46, 69, 1);
plotVerticalBar(66, 73, 1);
plotVerticalBar(86, 69, 1);
plotVerticalBar(106, 73, 1);
plotVerticalBar(126, 69, 1);
plotVerticalBar(6, 68, 0);
plotVerticalBar(26, 68, 0);
plotVerticalBar(46, 68, 0);
plotVerticalBar(66, 68, 0);
plotVerticalBar(86, 68, 0);
plotVerticalBar(106, 68, 0);
plotVerticalBar(126, 68, 0);
plotSpigot(0);
plotSpigot(12);
plotSpigot(24);
plotSpigot(36);
plotSpigot(48);
plotSpigot(60);
plotSpigot(87);
plotSpigot(99);
plotSpigot(111);
plotSpigot(123);
plotSpigot(135);
plotSpigot(147);
plotDataBlock(0, 0, 68, 6, 0, 0);
plotDataBlock(0, 6, 68, 18, 0, 2);
plotDataBlock(0, 24, 68, 18, 0, 4);
plotDataBlock(0, 42, 68, 18, 0, 6);
plotDataBlock(0, 60, 68, 18, 0, 8);
plotDataBlock(0, 78, 68, 18, 0, 10);
plotDataBlock(0, 96, 68, 18, 0, 12);
plotDataBlock(0, 114, 68, 6, 0, 14);
plotDataBlock(68, 0, 68, 6, 12, 0);
plotDataBlock(68, 6, 68, 18, 12, 2);
plotDataBlock(68, 24, 68, 18, 12, 4);
plotDataBlock(68, 42, 68, 18, 12, 6);
plotDataBlock(68, 60, 68, 18, 12, 8);
plotDataBlock(68, 78, 68, 18, 12, 10);
plotDataBlock(68, 96, 68, 18, 12, 12);
plotDataBlock(68, 114, 68, 6, 12, 14);
break;
case 9:
/* Version S */
plotHorizontalBar(5, 1);
plotHorizontalBar(7, 1);
setGridModule(6, 0);
setGridModule(6, this.symbol_width - 1);
resetGridModule(7, 1);
resetGridModule(7, this.symbol_width - 2);
switch (sub_version) {
case 1:
/* Version S-10 */
setGridModule(0, 5);
plotDataBlock(0, 0, 4, 5, 0, 0);
plotDataBlock(0, 5, 4, 5, 0, 1);
break;
case 2:
/* Version S-20 */
setGridModule(0, 10);
setGridModule(4, 10);
plotDataBlock(0, 0, 4, 10, 0, 0);
plotDataBlock(0, 10, 4, 10, 0, 1);
break;
case 3:
/* Version S-30 */
setGridModule(0, 15);
setGridModule(4, 15);
setGridModule(6, 15);
plotDataBlock(0, 0, 4, 15, 0, 0);
plotDataBlock(0, 15, 4, 15, 0, 1);
break;
}
break;
case 10:
/* Version T */
plotHorizontalBar(11, 1);
plotHorizontalBar(13, 1);
plotHorizontalBar(15, 1);
setGridModule(12, 0);
setGridModule(12, this.symbol_width - 1);
setGridModule(14, 0);
setGridModule(14, this.symbol_width - 1);
resetGridModule(13, 1);
resetGridModule(13, this.symbol_width - 2);
resetGridModule(15, 1);
resetGridModule(15, this.symbol_width - 2);
switch (sub_version) {
case 1:
/* Version T-16 */
setGridModule(0, 8);
setGridModule(10, 8);
plotDataBlock(0, 0, 10, 8, 0, 0);
plotDataBlock(0, 8, 10, 8, 0, 1);
break;
case 2:
/* Version T-32 */
setGridModule(0, 16);
setGridModule(10, 16);
setGridModule(12, 16);
plotDataBlock(0, 0, 10, 16, 0, 0);
plotDataBlock(0, 16, 10, 16, 0, 1);
break;
case 3:
/* Verion T-48 */
setGridModule(0, 24);
setGridModule(10, 24);
setGridModule(12, 24);
setGridModule(14, 24);
plotDataBlock(0, 0, 10, 24, 0, 0);
plotDataBlock(0, 24, 10, 24, 0, 1);
break;
}
break;
}
 
this.readable = "";
this.pattern = new String[this.row_count];
this.row_height = new int[this.row_count];
for (i = 0; i < this.row_count; i++) {
bin.setLength(0);
for (j = 0; j < this.symbol_width; j++) {
if (this.outputGrid[i][j]) {
bin.append('1');
} else {
bin.append('0');
}
}
this.pattern[i] = bin2pat(bin);
this.row_height[i] = 1;
}
}
 
private void logCodewords(final int count) {
info("Codewords: ");
for (int i = 0; i < count; i++) {
infoSpace(this.data[i]);
}
infoLine();
}
 
private int encodeAsCode1Data() {
Mode current_mode, next_mode;
boolean latch;
boolean done;
int sourcePoint, targetPoint, i, j;
int c40_p;
int text_p;
int edi_p;
int byte_start = 0;
final int[] c40_buffer = new int[6];
final int[] text_buffer = new int[6];
final int[] edi_buffer = new int[6];
String decimal_binary = "";
int length;
int shift_set, value;
int data_left, decimal_count;
int sub_value;
int bits_left_in_byte, target_count;
boolean isTwoDigits;
 
this.inputData = toBytes(this.content, StandardCharsets.ISO_8859_1);
length = this.inputData.length;
 
sourcePoint = 0;
targetPoint = 0;
c40_p = 0;
text_p = 0;
edi_p = 0;
 
if (this.inputDataType == DataType.GS1) {
this.data[targetPoint] = 232;
targetPoint++;
} /* FNC1 */
 
/* Step A */
current_mode = Mode.C1_ASCII;
next_mode = Mode.C1_ASCII;
 
do {
if (current_mode != next_mode) {
/* Change mode */
switch (next_mode) {
case C1_C40:
this.data[targetPoint] = 230;
targetPoint++;
break;
case C1_TEXT:
this.data[targetPoint] = 239;
targetPoint++;
break;
case C1_EDI:
this.data[targetPoint] = 238;
targetPoint++;
break;
case C1_BYTE:
this.data[targetPoint] = 231;
targetPoint++;
break;
}
}
 
if (current_mode != Mode.C1_BYTE && next_mode == Mode.C1_BYTE) {
byte_start = targetPoint;
}
current_mode = next_mode;
 
if (current_mode == Mode.C1_ASCII) { /* Step B - ASCII encodation */
next_mode = Mode.C1_ASCII;
 
if (length - sourcePoint >= 21) { /* Step B1 */
j = 0;
 
for (i = 0; i < 21; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 21) {
next_mode = Mode.C1_DECIMAL;
decimal_binary += "1111";
}
}
 
if (next_mode == Mode.C1_ASCII && length - sourcePoint >= 13) { /* Step B2 */
j = 0;
 
for (i = 0; i < 13; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 13) {
latch = false;
for (i = sourcePoint + 13; i < length; i++) {
if (!(this.inputData[i] >= '0' && this.inputData[i] <= '9')) {
latch = true;
}
}
 
if (!latch) {
next_mode = Mode.C1_DECIMAL;
decimal_binary += "1111";
}
}
}
 
if (next_mode == Mode.C1_ASCII) { /* Step B3 */
isTwoDigits = false;
if (sourcePoint + 1 != length) {
if (this.inputData[sourcePoint] >= '0' && this.inputData[sourcePoint] <= '9') {
if (this.inputData[sourcePoint + 1] >= '0' && this.inputData[sourcePoint + 1] <= '9') {
// remaining data consists of two numeric digits
this.data[targetPoint] = 10 * (this.inputData[sourcePoint] - '0') + this.inputData[sourcePoint + 1] - '0' + 130;
targetPoint++;
sourcePoint += 2;
isTwoDigits = true;
}
}
}
 
if (!isTwoDigits) {
if (this.inputData[sourcePoint] == FNC1) {
if (length - sourcePoint >= 15) { /* Step B4 */
j = 0;
 
for (i = 0; i < 15; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 15) {
this.data[targetPoint] = 236; /* FNC1 and change to Decimal */
targetPoint++;
sourcePoint++;
next_mode = Mode.C1_DECIMAL;
}
}
 
if (length - sourcePoint >= 7) { /* Step B5 */
j = 0;
 
for (i = 0; i < 7; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 7) {
latch = false;
for (i = sourcePoint + 7; i < length; i++) {
if (!(this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9')) {
latch = true;
}
}
 
if (!latch) {
this.data[targetPoint] = 236; /*
* FNC1 and change to Decimal
*/
targetPoint++;
sourcePoint++;
next_mode = Mode.C1_DECIMAL;
}
}
}
}
 
if (next_mode == Mode.C1_ASCII) {
 
/* Step B6 */
next_mode = lookAheadTest(length, sourcePoint, current_mode);
 
if (next_mode == Mode.C1_ASCII) {
if (this.inputData[sourcePoint] > 127) {
/* Step B7 */
this.data[targetPoint] = 235;
targetPoint++; /* FNC4 */
this.data[targetPoint] = this.inputData[sourcePoint] - 128 + 1;
targetPoint++;
sourcePoint++;
} else {
/* Step B8 */
if (this.inputData[sourcePoint] == FNC1) {
this.data[targetPoint] = 232;
targetPoint++;
sourcePoint++; /* FNC1 */
} else {
this.data[targetPoint] = this.inputData[sourcePoint] + 1;
targetPoint++;
sourcePoint++;
}
}
}
}
}
}
}
 
if (current_mode == Mode.C1_C40) { /* Step C - C40 encodation */
done = false;
next_mode = Mode.C1_C40;
if (c40_p == 0) {
if (length - sourcePoint >= 12) {
j = 0;
 
for (i = 0; i < 12; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 12) {
next_mode = Mode.C1_ASCII;
done = true;
}
}
 
if (length - sourcePoint >= 8) {
j = 0;
 
for (i = 0; i < 8; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (length - sourcePoint == 8) {
latch = true;
} else {
latch = true;
for (j = sourcePoint + 8; j < length; j++) {
if (this.inputData[j] <= '0' || this.inputData[j] >= '9') {
latch = false;
}
}
}
 
if (j == 8 && latch) {
next_mode = Mode.C1_ASCII;
done = true;
}
}
 
if (!done) {
next_mode = lookAheadTest(length, sourcePoint, current_mode);
}
}
 
if (next_mode != Mode.C1_C40) {
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
} else {
if (this.inputData[sourcePoint] > 127) {
c40_buffer[c40_p] = 1;
c40_p++;
c40_buffer[c40_p] = 30;
c40_p++; /* Upper Shift */
shift_set = C40_SHIFT[this.inputData[sourcePoint] - 128];
value = C40_VALUE[this.inputData[sourcePoint] - 128];
} else {
shift_set = C40_SHIFT[this.inputData[sourcePoint]];
value = C40_VALUE[this.inputData[sourcePoint]];
}
 
if (this.inputData[sourcePoint] == FNC1) {
shift_set = 2;
value = 27; /* FNC1 */
}
 
if (shift_set != 0) {
c40_buffer[c40_p] = shift_set - 1;
c40_p++;
}
c40_buffer[c40_p] = value;
c40_p++;
 
if (c40_p >= 3) {
int iv;
 
iv = 1600 * c40_buffer[0] + 40 * c40_buffer[1] + c40_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
 
c40_buffer[0] = c40_buffer[3];
c40_buffer[1] = c40_buffer[4];
c40_buffer[2] = c40_buffer[5];
c40_buffer[3] = 0;
c40_buffer[4] = 0;
c40_buffer[5] = 0;
c40_p -= 3;
}
sourcePoint++;
}
}
 
if (current_mode == Mode.C1_TEXT) { /* Step D - Text encodation */
done = false;
next_mode = Mode.C1_TEXT;
if (text_p == 0) {
if (length - sourcePoint >= 12) {
j = 0;
 
for (i = 0; i < 12; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 12) {
next_mode = Mode.C1_ASCII;
done = true;
}
}
 
if (length - sourcePoint >= 8) {
j = 0;
 
for (i = 0; i < 8; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (length - sourcePoint == 8) {
latch = true;
} else {
latch = true;
for (j = sourcePoint + 8; j < length; j++) {
if (this.inputData[j] <= '0' || this.inputData[j] >= '9') {
latch = false;
}
}
}
 
if (j == 8 && latch) {
next_mode = Mode.C1_ASCII;
done = true;
}
}
 
if (!done) {
next_mode = lookAheadTest(length, sourcePoint, current_mode);
}
}
 
if (next_mode != Mode.C1_TEXT) {
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
} else {
if (this.inputData[sourcePoint] > 127) {
text_buffer[text_p] = 1;
text_p++;
text_buffer[text_p] = 30;
text_p++; /* Upper Shift */
shift_set = TEXT_SHIFT[this.inputData[sourcePoint] - 128];
value = TEXT_VALUE[this.inputData[sourcePoint] - 128];
} else {
shift_set = TEXT_SHIFT[this.inputData[sourcePoint]];
value = TEXT_VALUE[this.inputData[sourcePoint]];
}
 
if (this.inputData[sourcePoint] == FNC1) {
shift_set = 2;
value = 27; /* FNC1 */
}
 
if (shift_set != 0) {
text_buffer[text_p] = shift_set - 1;
text_p++;
}
text_buffer[text_p] = value;
text_p++;
 
if (text_p >= 3) {
int iv;
 
iv = 1600 * text_buffer[0] + 40 * text_buffer[1] + text_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
 
text_buffer[0] = text_buffer[3];
text_buffer[1] = text_buffer[4];
text_buffer[2] = text_buffer[5];
text_buffer[3] = 0;
text_buffer[4] = 0;
text_buffer[5] = 0;
text_p -= 3;
}
sourcePoint++;
}
}
 
if (current_mode == Mode.C1_EDI) { /* Step E - EDI Encodation */
 
value = 0;
next_mode = Mode.C1_EDI;
if (edi_p == 0) {
if (length - sourcePoint >= 12) {
j = 0;
 
for (i = 0; i < 12; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 12) {
next_mode = Mode.C1_ASCII;
}
}
 
if (length - sourcePoint >= 8) {
j = 0;
 
for (i = 0; i < 8; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (length - sourcePoint == 8) {
latch = true;
} else {
latch = true;
for (j = sourcePoint + 8; j < length; j++) {
if (this.inputData[j] <= '0' || this.inputData[j] >= '9') {
latch = false;
}
}
}
 
if (j == 8 && latch) {
next_mode = Mode.C1_ASCII;
}
}
 
if (!(isEdiEncodable(this.inputData[sourcePoint]) && isEdiEncodable(this.inputData[sourcePoint + 1]) && isEdiEncodable(this.inputData[sourcePoint + 2]))) {
next_mode = Mode.C1_ASCII;
}
}
 
if (next_mode != Mode.C1_EDI) {
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
} else {
if (this.inputData[sourcePoint] == 13) {
value = 0;
}
if (this.inputData[sourcePoint] == '*') {
value = 1;
}
if (this.inputData[sourcePoint] == '>') {
value = 2;
}
if (this.inputData[sourcePoint] == ' ') {
value = 3;
}
if (this.inputData[sourcePoint] >= '0' && this.inputData[sourcePoint] <= '9') {
value = this.inputData[sourcePoint] - '0' + 4;
}
if (this.inputData[sourcePoint] >= 'A' && this.inputData[sourcePoint] <= 'Z') {
value = this.inputData[sourcePoint] - 'A' + 14;
}
 
edi_buffer[edi_p] = value;
edi_p++;
 
if (edi_p >= 3) {
int iv;
 
iv = 1600 * edi_buffer[0] + 40 * edi_buffer[1] + edi_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
 
edi_buffer[0] = edi_buffer[3];
edi_buffer[1] = edi_buffer[4];
edi_buffer[2] = edi_buffer[5];
edi_buffer[3] = 0;
edi_buffer[4] = 0;
edi_buffer[5] = 0;
edi_p -= 3;
}
sourcePoint++;
}
}
 
if (current_mode == Mode.C1_DECIMAL) { /* Step F - Decimal encodation */
 
next_mode = Mode.C1_DECIMAL;
 
data_left = length - sourcePoint;
decimal_count = 0;
 
if (data_left >= 1) {
if (this.inputData[sourcePoint] >= '0' && this.inputData[sourcePoint] <= '9') {
decimal_count = 1;
}
}
if (data_left >= 2) {
if (decimal_count == 1 && this.inputData[sourcePoint + 1] >= '0' && this.inputData[sourcePoint + 1] <= '9') {
decimal_count = 2;
}
}
if (data_left >= 3) {
if (decimal_count == 2 && this.inputData[sourcePoint + 2] >= '0' && this.inputData[sourcePoint + 2] <= '9') {
decimal_count = 3;
}
}
 
if (decimal_count != 3) {
 
/* Finish Decimal mode and go back to ASCII */
 
decimal_binary += "111111"; /* Unlatch */
 
target_count = 3;
if (decimal_binary.length() <= 16) {
target_count = 2;
}
if (decimal_binary.length() <= 8) {
target_count = 1;
}
bits_left_in_byte = 8 * target_count - decimal_binary.length();
if (bits_left_in_byte == 8) {
bits_left_in_byte = 0;
}
 
if (bits_left_in_byte == 2) {
decimal_binary += "01";
}
 
if (bits_left_in_byte == 4 || bits_left_in_byte == 6) {
if (decimal_count >= 1) {
sub_value = this.inputData[sourcePoint] - '0' + 1;
 
for (i = 0x08; i > 0; i = i >> 1) {
if ((sub_value & i) != 0) {
decimal_binary += "1";
} else {
decimal_binary += "0";
}
}
sourcePoint++;
} else {
decimal_binary += "1111";
}
}
 
if (bits_left_in_byte == 6) {
decimal_binary += "01";
}
 
/* Binary buffer is full - transfer to data */
if (target_count >= 1) {
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(i) == '1') {
this.data[targetPoint] += 128 >> i;
}
}
targetPoint++;
}
if (target_count >= 2) {
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(8 + i) == '1') {
this.data[targetPoint] += 128 >> i;
}
 
}
targetPoint++;
}
if (target_count == 3) {
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(16 + i) == '1') {
this.data[targetPoint] += 128 >> i;
}
 
}
targetPoint++;
}
 
next_mode = Mode.C1_ASCII;
} else {
/* There are three digits - convert the value to binary */
value = 100 * (this.inputData[sourcePoint] - '0') + 10 * (this.inputData[sourcePoint + 1] - '0') + this.inputData[sourcePoint + 2] - '0' + 1;
 
for (i = 0x200; i > 0; i = i >> 1) {
if ((value & i) != 0) {
decimal_binary += "1";
} else {
decimal_binary += "0";
}
}
sourcePoint += 3;
}
 
if (decimal_binary.length() >= 24) {
/* Binary buffer is full - transfer to data */
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(i) == '1') {
this.data[targetPoint] += 128 >> i;
}
 
if (decimal_binary.charAt(8 + i) == '1') {
this.data[targetPoint + 1] += 128 >> i;
}
 
if (decimal_binary.charAt(16 + i) == '1') {
this.data[targetPoint + 2] += 128 >> i;
}
 
}
targetPoint += 3;
 
if (decimal_binary.length() > 24) {
decimal_binary = decimal_binary.substring(24);
}
}
}
 
if (current_mode == Mode.C1_BYTE) {
next_mode = Mode.C1_BYTE;
 
if (this.inputData[sourcePoint] == FNC1) {
next_mode = Mode.C1_ASCII;
} else {
if (this.inputData[sourcePoint] <= 127) {
next_mode = lookAheadTest(length, sourcePoint, current_mode);
}
}
 
if (next_mode != Mode.C1_BYTE) {
/* Insert byte field length */
if (targetPoint - byte_start <= 249) {
for (i = targetPoint; i >= byte_start; i--) {
this.data[i + 1] = this.data[i];
}
this.data[byte_start] = targetPoint - byte_start;
targetPoint++;
} else {
for (i = targetPoint; i >= byte_start; i--) {
this.data[i + 2] = this.data[i];
}
this.data[byte_start] = 249 + (targetPoint - byte_start) / 250;
this.data[byte_start + 1] = (targetPoint - byte_start) % 250;
targetPoint += 2;
}
} else {
this.data[targetPoint] = this.inputData[sourcePoint];
targetPoint++;
sourcePoint++;
}
}
 
if (targetPoint > 1480) {
/* Data is too large for symbol */
throw new OkapiException("Input data too long");
}
 
} while (sourcePoint < length);
 
/* Empty buffers */
if (c40_p == 2) {
int iv;
 
c40_buffer[2] = 1;
iv = 1600 * c40_buffer[0] + 40 * c40_buffer[1] + c40_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
}
if (c40_p == 1) {
int iv;
 
c40_buffer[1] = 1;
c40_buffer[2] = 31; /* Pad */
iv = 1600 * c40_buffer[0] + 40 * c40_buffer[1] + c40_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
}
if (text_p == 2) {
int iv;
 
text_buffer[2] = 1;
iv = 1600 * text_buffer[0] + 40 * text_buffer[1] + text_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
}
if (text_p == 1) {
int iv;
 
text_buffer[1] = 1;
text_buffer[2] = 31; /* Pad */
iv = 1600 * text_buffer[0] + 40 * text_buffer[1] + text_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
}
 
if (current_mode == Mode.C1_DECIMAL) {
/* Finish Decimal mode and go back to ASCII */
 
decimal_binary += "111111"; /* Unlatch */
 
target_count = 3;
if (decimal_binary.length() <= 16) {
target_count = 2;
}
if (decimal_binary.length() <= 8) {
target_count = 1;
}
bits_left_in_byte = 8 * target_count - decimal_binary.length();
if (bits_left_in_byte == 8) {
bits_left_in_byte = 0;
}
 
if (bits_left_in_byte == 2) {
decimal_binary += "01";
}
 
if (bits_left_in_byte == 4 || bits_left_in_byte == 6) {
decimal_binary += "1111";
}
 
if (bits_left_in_byte == 6) {
decimal_binary += "01";
}
 
/* Binary buffer is full - transfer to data */
if (target_count >= 1) {
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(i) == '1') {
this.data[targetPoint] += 128 >> i;
}
}
targetPoint++;
}
if (target_count >= 2) {
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(8 + i) == '1') {
this.data[targetPoint] += 128 >> i;
}
}
targetPoint++;
}
if (target_count == 3) {
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(16 + i) == '1') {
this.data[targetPoint] += 128 >> i;
}
}
targetPoint++;
}
}
 
if (current_mode == Mode.C1_BYTE) {
/* Insert byte field length */
if (targetPoint - byte_start <= 249) {
for (i = targetPoint; i >= byte_start; i--) {
this.data[i + 1] = this.data[i];
}
this.data[byte_start] = targetPoint - byte_start;
targetPoint++;
} else {
for (i = targetPoint; i >= byte_start; i--) {
this.data[i + 2] = this.data[i];
}
this.data[byte_start] = 249 + (targetPoint - byte_start) / 250;
this.data[byte_start + 1] = (targetPoint - byte_start) % 250;
targetPoint += 2;
}
}
 
/* Re-check length of data */
if (targetPoint > 1480) {
/* Data is too large for symbol */