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/OperationExportPanel.java
74,13 → 74,13
public class OperationExportPanel extends JPanel {
 
@GuardedBy("EDT")
static private final DateFormat DF = new SimpleDateFormat("yyyyMMdd");
private static final DateFormat DF = new SimpleDateFormat("yyyyMMdd");
 
final JCheckBox lockedCheckBox = new JCheckBox("verrouillées uniquement");
final JButton bPrint = new JButton("Exporter");
 
public OperationExportPanel(final OperationCalendarManager manager, final List<SQLRowValues> rowsSite) {
lockedCheckBox.setSelected(true);
this.lockedCheckBox.setSelected(true);
//
this.setLayout(new GridBagLayout());
final GridBagConstraints c = new DefaultGridBagConstraints();
141,8 → 141,8
//
final JPanel p = new JPanel();
p.setLayout(new FlowLayout(FlowLayout.RIGHT));
p.add(lockedCheckBox);
p.add(bPrint);
p.add(this.lockedCheckBox);
p.add(this.bPrint);
c.gridwidth = 2;
c.gridx = 0;
c.gridy++;
151,7 → 151,7
this.add(p, c);
//
 
bPrint.addActionListener(new ActionListener() {
this.bPrint.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
160,9 → 160,9
}
final String statusVal = statusCombo.getValue();
final List<String> states = StringUtils.isEmpty(statusVal, true) ? null : Collections.singletonList(statusVal);
final List<JCalendarItem> items = manager.getItemIn(d1.getDate(), d2.getDate(), null, states);
final List<JCalendarItemDB> itemsToExport = new ArrayList<JCalendarItemDB>(items.size());
if (lockedCheckBox.isSelected()) {
final List<JCalendarItem> items = manager.getItemIn(d1.getDate(), d2.getDate(), manager.getAllUsers(), states);
final List<JCalendarItemDB> itemsToExport = new ArrayList<>(items.size());
if (OperationExportPanel.this.lockedCheckBox.isSelected()) {
for (JCalendarItem jCalendarItem : items) {
JCalendarItemDB i = (JCalendarItemDB) jCalendarItem;
if (i.getFlagsString().contains("locked")) {
176,12 → 176,12
}
}
if (rowsSite != null && !rowsSite.isEmpty()) {
final Set<String> allowedSites = new HashSet<String>();
final Set<String> allowedSites = new HashSet<>();
for (SQLRowValues r : rowsSite) {
String siteName = r.getString("NAME");
allowedSites.add(siteName);
}
final List<JCalendarItemDB> filtered = new ArrayList<JCalendarItemDB>(itemsToExport.size());
final List<JCalendarItemDB> filtered = new ArrayList<>(itemsToExport.size());
for (JCalendarItemDB i : itemsToExport) {
if (allowedSites.contains(i.getSiteName())) {
filtered.add(i);
218,9 → 218,9
});
}
 
static private final class Planner implements Comparable<Planner> {
private static final class Planner implements Comparable<Planner> {
 
static private final BigDecimal MS_PER_HOUR = BigDecimal.valueOf(1000 * 3600);
private static final BigDecimal MS_PER_HOUR = BigDecimal.valueOf(1000 * 3600);
 
private final String uid;
private final String xml;
256,7 → 256,7
 
final Element scheduleElem = doc.getRootElement().getChild("schedule");
this.startTime = new Date(Long.valueOf(scheduleElem.getAttributeValue("start")));
final long endTime = Long.valueOf(scheduleElem.getAttributeValue("end"));
final long endTime = Long.parseLong(scheduleElem.getAttributeValue("end"));
this.hours = DecimalUtils.round(BigDecimal.valueOf(endTime - this.startTime.getTime()).divide(MS_PER_HOUR, DecimalUtils.HIGH_PRECISION), 5);
} catch (Exception e) {
throw new IllegalStateException("couldn't get start for " + this.xml, e);
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationCalendarManager.java
62,6 → 62,10
this.userMngr = userMngr;
}
 
public UserManager getUserMngr() {
return userMngr;
}
 
public final SQLElementDirectory getDirectory() {
return this.dir;
}
389,4 → 393,15
}
return l.get(0);
}
 
/**
* Enabled or disabled users
*/
public List<User> getAllUsers() {
final List<User> result = new ArrayList<>();
for (User user : this.userMngr.getUsers().values()) {
result.add(user);
}
return result;
}
}
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/JCalendarItemDB.java
25,7 → 25,13
assert r.isFrozen();
this.item = r;
this.group = dir.getElement(this.item.getTable()).getContainer(this.item);
if (this.group == null) {
throw new IllegalArgumentException("no group found for row " + r);
}
this.operationElem = dir.getElement(OperationSQLElement.class);
if (this.operationElem == null) {
throw new IllegalStateException("no element found " + OperationSQLElement.class);
}
this.operation = CollectionUtils.getSole(this.group.getReferentRows(this.operationElem.getTable()));
}
 
84,7 → 90,7
}
 
public String getStatus() {
return status;
return this.status;
}
 
public void setOperationType(String type) {
92,7 → 98,7
}
 
public String getType() {
return type;
return this.type;
}
 
public String getFlagsString() {
109,7 → 115,7
}
 
public String getPlannerXML() {
return plannerXML;
return this.plannerXML;
}
 
public void setPlannerXML(String string) {
117,7 → 123,7
}
 
public String getPlannerUID() {
return plannerUID;
return this.plannerUID;
}
 
public void setPlannerUID(String plannerUID) {
125,7 → 131,7
}
 
public String getSiteName() {
return siteName;
return this.siteName;
}
 
public void setSiteName(String siteName) {
133,7 → 139,7
}
 
public String getSiteComment() {
return siteComment;
return this.siteComment;
}
 
public void setSiteComment(String siteComment) {
145,7 → 151,7
}
 
public Number getSiteId() {
return siteId;
return this.siteId;
}
 
public int getId() {
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationCalendarItemPrinter.java
11,9 → 11,11
 
public class OperationCalendarItemPrinter extends CalendarItemPrinter {
public static final Font FONT_LINE = new Font("Arial", Font.PLAIN, 9);
private List<JCalendarItem> itemsToWork;
 
public OperationCalendarItemPrinter(String title, List<JCalendarItem> items, PageFormat pf) {
public OperationCalendarItemPrinter(String title, List<JCalendarItem> items, PageFormat pf, List<JCalendarItem> itemsToWork) {
super(title, items, pf);
this.itemsToWork = itemsToWork;
}
 
@Override
38,7 → 40,7
 
@Override
public String getTitle() {
final List<JCalendarItem> items = this.getItems();
final List<JCalendarItem> items = this.itemsToWork;
int totalMinutes = 0;
for (JCalendarItem jCalendarItem : items) {
long t2 = jCalendarItem.getDtEnd().getTimeInMillis();
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationHistoryPanel.java
72,6 → 72,7
public void propertyChange(PropertyChangeEvent evt) {
final List<SQLRowValues> selectedRows = list.getSelectedRows();
final IListPanel listePanel = listHistoriquePanel.getListePanel(0);
// Activation/Desactivation des boutons Mofifier/Supprimer
if (selectedRows != null && !selectedRows.isEmpty()) {
final Set<Long> idsCalendarItemGroup = new HashSet<>();
for (SQLRowValues sqlRowValues : selectedRows) {
142,24 → 143,25
cal.set(Calendar.YEAR, selectedYear + 1);
Date dEnd = cal.getTime();
 
final SQLTable groupT = comboRequest.getPrimaryTable().getTable("CALENDAR_ITEM_GROUP");
final SQLTable calItemT = comboRequest.getPrimaryTable().getTable("CALENDAR_ITEM");
final SQLTable itemGroupTable = comboRequest.getPrimaryTable().getTable("CALENDAR_ITEM_GROUP");
final SQLTable itemTable = comboRequest.getPrimaryTable().getTable("CALENDAR_ITEM");
final SQLTable operationTable = comboRequest.getPrimaryTable().getTable("OPERATION");
final List<?> dateGroupIDs;
{
final SQLSelect copy = new SQLSelect(input);
copy.clearSelect();
copy.addSelect(copy.getAlias(groupT.getKey()));
copy.setWhere(copy.getAlias(comboRequest.getPrimaryTable().getTable("OPERATION").getField("ID_SITE")), "=", panel.getSelectedRow().getID());
final List<?> allGroupIDs = calItemT.getDBSystemRoot().getDataSource().executeCol(copy.asString());
 
copy.addSelect(copy.getAlias(itemGroupTable.getKey()));
copy.setWhere(copy.getAlias(operationTable.getField("ID_SITE")), "=", panel.getSelectedRow().getID());
final List<?> allGroupIDs = itemTable.getDBSystemRoot().getDataSource().executeCol(copy.asString());
final SQLSelect selIDGroup = new SQLSelect();
selIDGroup.addSelect(calItemT.getField("ID_CALENDAR_ITEM_GROUP"));
final Where where = new Where(calItemT.getField("START"), dStart, true, dEnd, true);
selIDGroup.setWhere(where).andWhere(new Where(calItemT.getField("ID_CALENDAR_ITEM_GROUP"), allGroupIDs));
dateGroupIDs = calItemT.getDBSystemRoot().getDataSource().executeCol(selIDGroup.asString());
selIDGroup.addSelect(itemTable.getField("ID_CALENDAR_ITEM_GROUP"));
final Where where = new Where(itemTable.getField("START"), dStart, true, dEnd, true);
selIDGroup.setWhere(where).andWhere(new Where(itemTable.getField("ID_CALENDAR_ITEM_GROUP"), allGroupIDs));
dateGroupIDs = itemTable.getDBSystemRoot().getDataSource().executeCol(selIDGroup.asString());
}
 
input.setWhere(new Where(input.getAlias(groupT.getKey()), dateGroupIDs));
Where w = new Where(input.getAlias(itemGroupTable.getKey()), dateGroupIDs);
input.setWhere(w);
} catch (Throwable e) {
e.printStackTrace();
}
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/ModuleOperation.java
62,8 → 62,6
// SQLRequestLog.showFrame();
TemplateManager.getInstance().register(OPERATIONS_REPORT_TEMPLATE_ID);
TemplateManager.getInstance().register(OPERATIONS_REPORT_TEMPLATE2_ID);
// Translation loading
TranslationManager.getInstance().addTranslationStreamFromClass(this.getClass());
}
 
@Override
148,6 → 146,10
createTableOperation.addVarCharColumn("DESCRIPTION", 10000);
createTableOperation.addVarCharColumn("PLANNER_UID", 2048);
createTableOperation.addVarCharColumn("PLANNER_XML", 2048);
 
ctxt.executeSQL();
// SQLTable.setUndefID(ctxt.getRoot().getSchema(), TABLE_SITE, null);
// SQLTable.setUndefID(ctxt.getRoot().getSchema(), TABLE_OPERATION, null);
}
}
 
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/CalendarPrintPanel.java
43,6 → 43,8
private static final double POINTS_PER_INCH = 72.0;
 
public CalendarPrintPanel(final OperationCalendarManager manager, final int week, final int year, final List<User> selectedUsers, final List<String> selectedStates) {
 
System.err.println("CalendarPrintPanel.CalendarPrintPanel()" + selectedUsers);
preview.setSelected(true);
//
this.setLayout(new GridBagLayout());
109,8 → 111,15
});
final PageFormat pf = new PageFormat();
pf.setPaper(new A4());
final CalendarItemPrinter printable = new OperationCalendarItemPrinter(user.getFullName(), itemInWeek, pf);
List<JCalendarItem> itemsToWork = new ArrayList<>();
for (JCalendarItem item : itemInWeek) {
if (!item.hasFlag(ModuleOperation.FREE_TIME_FLAG)) {
itemsToWork.add(item);
}
}
 
final CalendarItemPrinter printable = new OperationCalendarItemPrinter(user.getFullName(), itemInWeek, pf, itemsToWork);
 
p.add(printable);
}
}
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationCalendarPanel.java
117,12 → 117,11
this.beginStateSaving(conf.getConfDir(), w);
}
 
public static Map<Integer, Long> getDurations(List<List<JCalendarItem>> list, final Flag requiredFlag) {
public static Map<Integer, Long> getDurations(List<List<JCalendarItem>> list, final Flag requiredFlag, final Flag excludedFlag) {
final Map<Integer, Long> res = new HashMap<>();
final Flag freeTimeFlag = ModuleOperation.FREE_TIME_FLAG;
for (List<JCalendarItem> items : list) {
for (JCalendarItem item : items) {
if (!item.hasFlag(freeTimeFlag) && (requiredFlag == null || item.hasFlag(requiredFlag)) && item.getCookie() instanceof SQLRowValues) {
if (!item.hasFlag(excludedFlag) && (requiredFlag == null || item.hasFlag(requiredFlag)) && item.getCookie() instanceof SQLRowValues) {
final SQLRowValues user = (SQLRowValues) item.getCookie();
final long toAddMinutes = (item.getDtEnd().getTimeInMillis() - item.getDtStart().getTimeInMillis()) / (60 * 1000);
final Integer key = user.getID();
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/UserOperationListModel.java
140,7 → 140,7
return this.usersAndWeeklyMinutes;
}
}
final Map<User, Integer> uInfo = new LinkedHashMap<User, Integer>();
final Map<User, Integer> uInfo = new LinkedHashMap<>();
final SQLRowValues v = new SQLRowValues(this.salarieElem.getTable());
v.putNulls("NOM", "PRENOM");
v.putRowValues("ID_INFOS_SALARIE_PAYE").putNulls("DUREE_HEBDO");
152,10 → 152,14
for (int i = 0; i < size; i++) {
final User u = users.get(i);
final String name = u.getName().trim();
final String firstName = u.getFirstName();
final String firstName = u.getFirstName().trim();
Integer minutes = null;
for (SQLRowValues row : rows) {
if (row.getString("NOM").trim().equalsIgnoreCase(name) && row.getString("PRENOM").trim().equalsIgnoreCase(firstName)) {
// Matching Utilisateur <-> Salarié
// Nom et prénom identique
final String sName = row.getString("NOM").trim();
final String sFirstName = row.getString("PRENOM").trim();
if (sName.equalsIgnoreCase(name) && sFirstName.equalsIgnoreCase(firstName)) {
minutes = (int) row.getForeign("ID_INFOS_SALARIE_PAYE").getFloat("DUREE_HEBDO") * 60;
break;
}
169,8 → 173,8
}
 
private void setDurations(final List<List<JCalendarItem>> viewItems) {
final Map<Integer, Long> all = OperationCalendarPanel.getDurations(viewItems, null);
final Map<Integer, Long> locked = OperationCalendarPanel.getDurations(viewItems, Flag.getFlag("locked"));
final Map<Integer, Long> all = OperationCalendarPanel.getDurations(viewItems, null, ModuleOperation.FREE_TIME_FLAG);
final Map<Integer, Long> locked = OperationCalendarPanel.getDurations(viewItems, Flag.getFlag("locked"), ModuleOperation.FREE_TIME_FLAG);
synchronized (this) {
this.allDurations = Collections.unmodifiableMap(all);
this.lockedDurations = Collections.unmodifiableMap(locked);
217,10 → 221,10
// not a SALARIE
suffix = "";
} else {
// Durée verrouillée
final int d2 = getDuration(locked, u.getId());
// Durée planifiée
final int d = getDuration(all, u.getId());
// Durée verrouillée
final int d2 = getDuration(locked, u.getId());
suffix = " [" + formatDuration(d2) + " / " + formatDuration(d) + " / " + formatDuration(weeklyMinutes) + "]";
}
res.add(createItem(u, (u.getFullName() + suffix).trim()));
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/UserColor.java
18,12 → 18,16
final int size = users.size();
for (int i = 0; i < size; i++) {
final User u = users.get(i);
map.put(u.getId(), Color.decode(COLORS[i % COLORS.length]));
if (u.getColor() == null) {
this.map.put(u.getId(), Color.decode(COLORS[i % COLORS.length]));
} else {
this.map.put(u.getId(), u.getColor());
}
}
}
 
public synchronized Color getColor(int id) {
return map.get(id);
return this.map.get(id);
}
 
public static final synchronized UserColor getInstance() {
/trunk/Modules/Module 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/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 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 Project/src/org/openconcerto/modules/project/Module.java
94,7 → 94,6
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.IExnClosure;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.i18n.TranslationManager;
 
public final class Module extends AbstractModule {
 
191,7 → 190,6
@Override
protected void setupElements(final SQLElementDirectory dir) {
super.setupElements(dir);
TranslationManager.getInstance().addTranslationStreamFromClass(this.getClass());
dir.addSQLElement(ProjectSQLElement.class);
dir.addSQLElement(ProjectStateSQLElement.class);
dir.addSQLElement(ProjectTypeSQLElement.class);
607,7 → 605,7
protected void setupComponents(final ComponentsContext ctxt) {
 
DBRoot root = ComptaPropsConfiguration.getInstanceCompta().getRootSociete();
List<String> table2check = Arrays.asList("BON_RECEPTION", "DEMANDE_PRIX", "DEMANDE_ACHAT_ELEMENT");
List<String> table2check = Arrays.asList("BON_RECEPTION", "DEMANDE_PRIX", "DEMANDE_ACHAT_ELEMENT", "FACTURE_FOURNISSEUR");
for (String table : table2check) {
if (root.contains(table)) {
SQLTable tableCR = root.getTable(table);
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/ExpenseGroup.java
7,32 → 7,31
public class ExpenseGroup extends Group {
 
public ExpenseGroup() {
super(ExpenseSQLElement.ELEMENT_CODE + ".default");
super(ExpenseSQLElement.ELEMENT_CODE + ".default", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
final Group g = new Group(ExpenseSQLElement.ELEMENT_CODE + ".identifier");
g.addItem("DATE");
g.addItem("ID_USER_COMMON");
g.addItem("DATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
g.addItem("ID_USER_COMMON", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
 
this.add(g);
 
final Group gDescription = new Group(ExpenseSQLElement.ELEMENT_CODE + ".description");
gDescription.add(new Item("DESCRIPTION", new LayoutHints(true, true, true, true, true, true)));
gDescription.addItem("DESCRIPTION", new LayoutHints(true, true, true, true, true, true, true, true));
gDescription.addItem("ID_EXPENSE_STATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(gDescription);
 
final Group gTravel = new Group(ExpenseSQLElement.ELEMENT_CODE + ".travel", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gTravel.addItem("TRAVEL_DISTANCE");
gTravel.addItem("TRAVEL_RATE");
gTravel.addItem("TRAVEL_AMOUNT");
final Group gTravel = new Group(ExpenseSQLElement.ELEMENT_CODE + ".travel",
LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gTravel.addItem("TRAVEL_DISTANCE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gTravel.addItem("TRAVEL_RATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gTravel.addItem("TRAVEL_AMOUNT", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(gTravel);
 
final Group gAddress = new Group(ExpenseSQLElement.ELEMENT_CODE + ".misc", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gAddress.addItem("MISC_AMOUNT");
final Group gAddress = new Group(ExpenseSQLElement.ELEMENT_CODE + ".misc",
LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gAddress.addItem("MISC_AMOUNT", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
// gAddress.addItem("ID_EXPENSE_STATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(gAddress);
 
final Group gState = new Group(ExpenseSQLElement.ELEMENT_CODE + ".state");
gState.addItem("ID_EXPENSE_STATE");
 
this.add(gState);
 
}
 
}
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/translation_fr.xml
New file
0,0 → 1,5
<translation lang="fr">
<item id="humanresources.travel.expense.travel" label="Frais kilometriques" />
<item id="humanresources.travel.expense.misc" label="Frais annexes" />
<item id="humanresources.travel.expense.default" label="Description" />
</translation>
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/labels_fr.xml
1,7 → 1,7
<?xml version="1.0" encoding="UTF-8" ?>
<ROOT>
<TABLE name="EXPENSE_STATE">
<FIELD name="NAME" label="Nom" />
<FIELD name="NAME" label="Statut" />
</TABLE>
<TABLE name="EXPENSE">
<FIELD name="NAME" label="Nom" />
12,9 → 12,9
<FIELD name="TRAVEL_RATE" label="Tarif kilométrique" />
<FIELD name="TRAVEL_AMOUNT" label="Montant des frais kilométriques" />
<FIELD name="MISC_AMOUNT" label="Montant des frais annexes" />
<FIELD name="ID_EXPENSE_STATE" label="Status" />
<FIELD name="ID_EXPENSE_STATE" label="Statut" />
<FIELD name="ID_USER_COMMON" label="Employé" />
<FIELD name="ID_EXPENSE_STATE" label="Status" />
<FIELD name="ID_EXPENSE_STATE" label="Statut" />
</TABLE>
 
</ROOT>
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/ExpenseSQLComponent.java
7,6 → 7,7
import java.util.Set;
 
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.text.AbstractDocument;
import javax.swing.text.JTextComponent;
 
13,9 → 14,10
import org.openconcerto.erp.core.common.ui.DeviseField;
import org.openconcerto.sql.element.GroupSQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.sqlobject.ElementComboBox;
import org.openconcerto.sql.sqlobject.itemview.VWRowItemView;
import org.openconcerto.ui.JDate;
import org.openconcerto.ui.component.ITextArea;
import org.openconcerto.ui.JLabelBold;
import org.openconcerto.ui.component.text.TextComponentUtils;
import org.openconcerto.ui.group.Group;
import org.openconcerto.utils.text.DocumentFilterList;
41,11 → 43,13
}
};
 
final AbstractDocument comp1 = (AbstractDocument) TextComponentUtils.getDocument(getView("TRAVEL_DISTANCE").getComp());
final AbstractDocument comp1 = (AbstractDocument) TextComponentUtils
.getDocument(getView("TRAVEL_DISTANCE").getComp());
DocumentFilterList.add(comp1, new LimitedSizeDocumentFilter(5), FilterType.SIMPLE_FILTER);
getView("TRAVEL_DISTANCE").addValueListener(listener);
 
final AbstractDocument comp2 = (AbstractDocument) TextComponentUtils.getDocument(getView("TRAVEL_RATE").getComp());
final AbstractDocument comp2 = (AbstractDocument) TextComponentUtils
.getDocument(getView("TRAVEL_RATE").getComp());
DocumentFilterList.add(comp2, new LimitedSizeDocumentFilter(5), FilterType.SIMPLE_FILTER);
 
getView("TRAVEL_RATE").addValueListener(listener);
62,17 → 66,33
}
 
@Override
public JComponent getEditor(String id) {
if (id.equals("DESCRIPTION")) {
return new ITextArea();
} else if (id.equals("DATE")) {
public JComponent createEditor(String id) {
if(id.equals("ID_USER_COMMON") || id.equals("ID_EXPENSE_STATE")) {
ElementComboBox comp = new ElementComboBox(false, 15);
((ElementComboBox) comp).init(getElement().getForeignElement(id));
return comp;
}
else if (id.equals("DATE")) {
return new JDate(true);
} else if (id.endsWith("AMOUNT")) {
return new DeviseField();
}
return super.getEditor(id);
return super.createEditor(id);
}
 
@Override
public JLabel getLabel(String id) {
if (id.equals("humanresources.travel.expense.travel")) {
return new JLabelBold("Frais kilometriques");
} else if (id.equals("humanresources.travel.expense.default")) {
return new JLabelBold("Descriptif");
} else if (id.equals("humanresources.travel.expense.misc")) {
return new JLabelBold("Frais annexes");
} else {
return (JLabel) super.getLabel(id);
}
}
 
private void updateAmount() {
float v1 = getFloat("TRAVEL_DISTANCE");
float v2 = getFloat("TRAVEL_RATE");
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/BatchDetailPanel.java
22,46 → 22,47
this.setLayout(new VFlowLayout());
}
 
public void setField(SQLField field) {
public void setField(BatchField batchField) {
this.removeAll();
 
SQLField field = batchField.getField();
final SQLType type = field.getType();
final Class<?> javaType = type.getJavaType();
final String fName = field.getName();
if (fName.equals("PV_TTC")) {
final NumberProcessor p = new TTCProcessor(field);
final NumberProcessor p = new TTCProcessor(batchField);
this.add(p);
this.processor = p;
} else if (fName.equals("PV_HT")) {
final NumberProcessor p = new HTProcessor(field);
final NumberProcessor p = new HTProcessor(batchField);
this.add(p);
this.processor = p;
} else if (fName.equals("ID_TAXE")) {
final ReferenceProcessor p = new TVAProcessor(field);
final ReferenceProcessor p = new TVAProcessor(batchField);
this.add(p);
this.processor = p;
} else if (fName.equals("PA_HT")) {
final NumberProcessor p = new PurchaseProcessor(field);
final NumberProcessor p = new PurchaseProcessor(batchField);
this.add(p);
this.processor = p;
} else if (javaType.equals(Boolean.class)) {
final BooleanProcessor p = new BooleanProcessor(field);
final BooleanProcessor p = new BooleanProcessor(batchField);
this.add(p);
this.processor = p;
} else if (field.isKey()) {
final ReferenceProcessor p = new ReferenceProcessor(field);
final ReferenceProcessor p = new ReferenceProcessor(batchField);
this.add(p);
this.processor = p;
} else if (javaType.equals(String.class)) {
final StringProcessor p = new StringProcessor(field);
final StringProcessor p = new StringProcessor(batchField);
this.add(p);
this.processor = p;
} else if (javaType.equals(Date.class)) {
final DateProcessor p = new DateProcessor(field);
final DateProcessor p = new DateProcessor(batchField);
this.add(p);
this.processor = p;
} else if (javaType.equals(BigDecimal.class) || javaType.equals(Float.class) || javaType.equals(Double.class) || javaType.equals(Integer.class) || javaType.equals(Long.class)) {
final NumberProcessor p = new NumberProcessor(field);
final NumberProcessor p = new NumberProcessor(batchField);
this.add(p);
this.processor = p;
}
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/BatchField.java
New file
0,0 → 1,58
package org.openconcerto.modules.common.batchprocessing;
 
import java.util.List;
 
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.SQLFieldTranslator;
 
public class BatchField {
 
private final SQLField field;
private final SQLRowAccessor foreignLinkRow;
private final SQLFieldTranslator translator;
private final SQLElement elementLink;
 
public BatchField(SQLElementDirectory dir, SQLField field, SQLRowAccessor foreignLinkRow) {
this.field = field;
this.foreignLinkRow = foreignLinkRow;
 
this.translator = dir.getTranslator();
if (foreignLinkRow == null) {
this.elementLink = null;
} else {
this.elementLink = dir.getElement(foreignLinkRow.getTable());
}
}
 
public SQLField getField() {
return field;
}
 
public SQLRowAccessor getForeignLinkRow() {
return foreignLinkRow;
}
 
public String getComboName() {
if (this.foreignLinkRow == null) {
return this.translator.getLabelFor(this.field);
} else {
return this.elementLink.getPluralName() + " " + this.foreignLinkRow.getString("NOM") + " " + this.translator.getLabelFor(this.field);
}
}
 
public List<SQLRow> getReferentRows(SQLRowAccessor rowOrigin) {
SQLSelect sel = new SQLSelect();
sel.addSelectStar(this.field.getTable());
final Where w = new Where(this.field.getTable().getField("ID_" + rowOrigin.getTable().getName()), "=", rowOrigin.getID());
sel.setWhere(w.and(new Where(this.field.getTable().getField("ID_" + foreignLinkRow.getTable().getName()), "=", foreignLinkRow.getID())));
return SQLRowListRSH.execute(sel);
}
 
}
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/Module.java
2,7 → 2,6
 
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.List;
 
9,18 → 8,12
import javax.swing.AbstractAction;
import javax.swing.JFrame;
 
import org.openconcerto.erp.config.Gestion;
import org.openconcerto.erp.modules.AbstractModule;
import org.openconcerto.erp.modules.ComponentsContext;
import org.openconcerto.erp.modules.ModuleFactory;
import org.openconcerto.erp.modules.ModuleManager;
import org.openconcerto.erp.modules.ModulePackager;
import org.openconcerto.erp.modules.RuntimeModuleFactory;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRequestLog;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.ui.ConnexionPanel;
import org.openconcerto.sql.view.list.IListe;
import org.openconcerto.sql.view.list.IListeAction.IListeEvent;
import org.openconcerto.sql.view.list.RowAction;
33,7 → 26,7
}
 
@Override
protected void setupComponents(ComponentsContext ctxt) {
protected void setupComponents(final ComponentsContext ctxt) {
 
super.setupComponents(ctxt);
final SQLElement element = ctxt.getElement("ARTICLE");
60,7 → 53,7
 
};
 
f.setContentPane(new BatchEditorPanel(rows, filter));
f.setContentPane(new BatchEditorPanel(ctxt.getElement("ARTICLE").getDirectory(), rows, filter));
f.pack();
f.setMinimumSize(new Dimension(400, 300));
f.setLocationRelativeTo(IListe.get(e));
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/NumberProcessor.java
13,7 → 13,7
import javax.swing.JRadioButton;
import javax.swing.JTextField;
 
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.ui.DefaultGridBagConstraints;
20,7 → 20,7
 
public class NumberProcessor extends JPanel implements BatchProcessor {
 
private final SQLField field;
private final BatchField batchfield;
// Editors
final JTextField tReplace = new JTextField();
private JRadioButton bReplace;
30,8 → 30,8
final JTextField tRemove = new JTextField();
private JRadioButton bRemove;
 
public NumberProcessor(SQLField field) {
this.field = field;
public NumberProcessor(BatchField field) {
this.batchfield = field;
 
this.setLayout(new GridBagLayout());
bReplace = new JRadioButton("remplacer par");
110,11 → 110,23
if (bReplace.isSelected()) {
BigDecimal v = new BigDecimal(this.tReplace.getText().trim());
for (SQLRowAccessor sqlRowAccessor : r) {
 
if (batchfield.getForeignLinkRow() != null) {
 
final List<SQLRow> referentRow = batchfield.getReferentRows(sqlRowAccessor);
for (SQLRow sqlRowT : referentRow) {
SQLRowValues rowValues = sqlRowT.createEmptyUpdateRow();
rowValues.put(batchfield.getField().getName(), decimalToFieldType(v));
processBeforeUpdate(sqlRowT, rowValues);
rowValues.update();
}
} else {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
rowValues.put(field.getName(), decimalToFieldType(v));
rowValues.put(batchfield.getField().getName(), decimalToFieldType(v));
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
}
} else if (bAdd.isSelected()) {
 
String t = this.tAdd.getText().trim();
127,18 → 139,44
BigDecimal v = new BigDecimal(t);
 
for (SQLRowAccessor sqlRowAccessor : r) {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
final BigDecimal value = sqlRowAccessor.asRow().getBigDecimal(field.getName());
 
if (batchfield.getForeignLinkRow() != null) {
 
final List<SQLRow> referentRow = batchfield.getReferentRows(sqlRowAccessor);
for (SQLRow sqlRowT : referentRow) {
 
SQLRowValues rowValues = sqlRowT.createEmptyUpdateRow();
BigDecimal value = sqlRowT.asRow().getBigDecimal(batchfield.getField().getName());
 
if (value != null) {
if (isPercent) {
rowValues.put(field.getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(100)).add(BigDecimal.ONE))));
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(100)).add(BigDecimal.ONE))));
} else {
rowValues.put(field.getName(), decimalToFieldType(value.add(v)));
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.add(v)));
}
processBeforeUpdate(sqlRowT, rowValues);
rowValues.update();
}
}
} else {
 
final SQLRowValues rowValues;
final BigDecimal value;
 
rowValues = sqlRowAccessor.createEmptyUpdateRow();
value = sqlRowAccessor.asRow().getBigDecimal(batchfield.getField().getName());
 
if (value != null) {
if (isPercent) {
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(100)).add(BigDecimal.ONE))));
} else {
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.add(v)));
}
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
}
}
} else if (bRemove.isSelected()) {
String t = this.tRemove.getText().trim();
boolean isPercent = false;
149,15 → 187,35
 
BigDecimal v = new BigDecimal(t);
for (SQLRowAccessor sqlRowAccessor : r) {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
if (batchfield.getForeignLinkRow() != null) {
final List<SQLRow> referentRow = batchfield.getReferentRows(sqlRowAccessor);
if (referentRow != null && !referentRow.isEmpty()) {
for (SQLRow sqlRowT : referentRow) {
 
final BigDecimal value = sqlRowAccessor.asRow().getBigDecimal(field.getName());
SQLRowValues rowValues = sqlRowT.createEmptyUpdateRow();
final BigDecimal value = sqlRowT.getBigDecimal(batchfield.getField().getName());
if (value != null) {
if (isPercent) {
rowValues.put(field.getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(-100)).add(BigDecimal.ONE))));
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(-100)).add(BigDecimal.ONE))));
} else {
rowValues.put(field.getName(), decimalToFieldType(value.add(v)));
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.add(v)));
}
processBeforeUpdate(sqlRowT, rowValues);
rowValues.update();
}
}
}
} else {
 
SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
final BigDecimal value = sqlRowAccessor.asRow().getBigDecimal(batchfield.getField().getName());
 
if (value != null) {
if (isPercent) {
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.multiply(v.divide(new BigDecimal(-100)).add(BigDecimal.ONE))));
} else {
rowValues.put(batchfield.getField().getName(), decimalToFieldType(value.add(v)));
}
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
164,9 → 222,10
}
}
}
}
 
private Object decimalToFieldType(BigDecimal v) {
final Class<?> javaType = field.getType().getJavaType();
final Class<?> javaType = batchfield.getField().getType().getJavaType();
if (javaType.equals(BigDecimal.class)) {
return v;
} else if (javaType.equals(Float.class)) {
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/ReferenceProcessor.java
18,18 → 18,18
 
public class ReferenceProcessor extends JPanel implements BatchProcessor {
 
private final SQLField field;
private final BatchField field;
final SQLElement element;
private ElementComboBox combo;
 
public ReferenceProcessor(SQLField field) {
public ReferenceProcessor(BatchField field) {
this.field = field;
this.element = ComptaPropsConfiguration.getInstanceCompta().getDirectory().getElement(field.getForeignTable());
this.element = ComptaPropsConfiguration.getInstanceCompta().getDirectory().getElement(field.getField().getForeignTable());
 
if (element != null) {
this.setLayout(new BorderLayout());
this.add(new JLabel("remplacer par "), BorderLayout.WEST);
combo = new ElementComboBox(true, 200);
combo = new ElementComboBox(true, 10);
combo.setMinimal();
combo.setAddIconVisible(false);
combo.init(element);
36,7 → 36,7
this.add(combo, BorderLayout.CENTER);
} else {
this.setLayout(new FlowLayout());
this.add(new JLabelWarning("No element for table " + field.getTable().getName()));
this.add(new JLabelWarning("No element for table " + field.getField().getTable().getName()));
}
}
 
45,7 → 45,7
 
for (SQLRowAccessor sqlRowAccessor : r) {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
rowValues.put(field.getName(), combo.getSelectedId());
rowValues.put(field.getField().getName(), combo.getSelectedId());
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/product/PurchaseProcessor.java
2,14 → 2,14
 
import java.math.BigDecimal;
 
import org.openconcerto.modules.common.batchprocessing.BatchField;
import org.openconcerto.modules.common.batchprocessing.NumberProcessor;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
 
public class PurchaseProcessor extends NumberProcessor {
 
public PurchaseProcessor(SQLField field) {
public PurchaseProcessor(BatchField field) {
super(field);
}
 
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/product/TTCProcessor.java
4,8 → 4,8
 
import org.openconcerto.erp.core.finance.tax.model.TaxeCache;
import org.openconcerto.erp.utils.ConvertDevise;
import org.openconcerto.modules.common.batchprocessing.BatchField;
import org.openconcerto.modules.common.batchprocessing.NumberProcessor;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
12,7 → 12,7
 
public class TTCProcessor extends NumberProcessor {
 
public TTCProcessor(SQLField field) {
public TTCProcessor(BatchField field) {
super(field);
}
 
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/product/TVAProcessor.java
4,8 → 4,8
 
import org.openconcerto.erp.core.finance.tax.model.TaxeCache;
import org.openconcerto.erp.utils.ConvertDevise;
import org.openconcerto.modules.common.batchprocessing.BatchField;
import org.openconcerto.modules.common.batchprocessing.ReferenceProcessor;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
12,7 → 12,7
 
public class TVAProcessor extends ReferenceProcessor {
 
public TVAProcessor(SQLField field) {
public TVAProcessor(BatchField field) {
super(field);
}
 
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/product/HTProcessor.java
4,8 → 4,8
 
import org.openconcerto.erp.core.finance.tax.model.TaxeCache;
import org.openconcerto.erp.utils.ConvertDevise;
import org.openconcerto.modules.common.batchprocessing.BatchField;
import org.openconcerto.modules.common.batchprocessing.NumberProcessor;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
12,7 → 12,7
 
public class HTProcessor extends NumberProcessor {
 
public HTProcessor(SQLField field) {
public HTProcessor(BatchField field) {
super(field);
}
 
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/StringProcessor.java
29,8 → 29,8
private JRadioButton bLower;
private JRadioButton bUpper;
 
public StringProcessor(SQLField field) {
this.field = field;
public StringProcessor(BatchField field) {
this.field = field.getField();
 
this.setLayout(new GridBagLayout());
bReplace = new JRadioButton("remplacer par");
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/BatchEditorPanel.java
24,10 → 24,13
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.PropsConfiguration;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.request.SQLFieldTranslator;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.JLabelBold;
36,24 → 39,34
 
public class BatchEditorPanel extends JPanel {
 
public BatchEditorPanel(final List<SQLRowValues> rows, FieldFilter filter) {
Configuration conf = PropsConfiguration.getInstance();
final SQLFieldTranslator translator = conf.getTranslator();
public BatchEditorPanel(final SQLElementDirectory dir, final List<SQLRowValues> rows, FieldFilter filter) {
SQLFieldTranslator translator = dir.getTranslator();
Set<SQLField> fields = rows.get(0).getTable().getFields();
List<SQLField> f = new ArrayList<SQLField>();
List<BatchField> f = new ArrayList<BatchField>();
for (SQLField sqlField : fields) {
if (ForbiddenFieldName.isAllowed(sqlField.getName()) && translator.getLabelFor(sqlField) != null) {
if (filter == null || !filter.isFiltered(sqlField)) {
f.add(sqlField);
f.add(new BatchField(dir, sqlField, null));
}
}
}
SQLTable tableTarif = rows.get(0).getTable().getTable("TARIF");
SQLTable tableArticleTarif = rows.get(0).getTable().getTable("ARTICLE_TARIF");
SQLSelect sel = new SQLSelect();
sel.addSelectStar(tableTarif);
List<SQLRow> rowTarif = SQLRowListRSH.execute(sel);
for (SQLRow sqlRow : rowTarif) {
f.add(new BatchField(dir, tableArticleTarif.getField("PV_HT"), sqlRow));
if (tableArticleTarif.contains("POURCENT_REMISE")) {
f.add(new BatchField(dir, tableArticleTarif.getField("POURCENT_REMISE"), sqlRow));
}
}
 
Collections.sort(f, new Comparator<SQLField>() {
Collections.sort(f, new Comparator<BatchField>() {
 
@Override
public int compare(SQLField o1, SQLField o2) {
return translator.getLabelFor(o1).compareToIgnoreCase(translator.getLabelFor(o2));
public int compare(BatchField o1, BatchField o2) {
return o1.getComboName().compareToIgnoreCase(o2.getComboName());
}
});
this.setLayout(new GridBagLayout());
60,11 → 73,11
GridBagConstraints c = new DefaultGridBagConstraints();
this.add(new JLabel("Champ"), c);
 
final JComboBox<SQLField> combo = new JComboBox<SQLField>(f.toArray(new SQLField[1]));
final JComboBox<BatchField> combo = new JComboBox<BatchField>(f.toArray(new BatchField[1]));
combo.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
String label = translator.getLabelFor(((SQLField) value));
String label = ((BatchField) value).getComboName();
return super.getListCellRendererComponent(list, label, index, isSelected, cellHasFocus);
}
});
86,7 → 99,7
c.gridy++;
c.anchor = GridBagConstraints.NORTHWEST;
final BatchDetailPanel comp = new BatchDetailPanel();
comp.setField((SQLField) combo.getSelectedItem());
comp.setField((BatchField) combo.getSelectedItem());
this.add(comp, c);
 
JPanel actions = new JPanel();
108,7 → 121,7
 
@Override
public void itemStateChanged(ItemEvent e) {
comp.setField((SQLField) combo.getSelectedItem());
comp.setField((BatchField) combo.getSelectedItem());
 
}
});
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/DateProcessor.java
17,8 → 17,8
private final JDate d = new JDate(true);
private final SQLField field;
 
public DateProcessor(SQLField field) {
this.field = field;
public DateProcessor(BatchField field) {
this.field = field.getField();
this.setLayout(new FlowLayout());
this.add(new JLabel("forcer la date au "));
this.add(d);
/trunk/Modules/Module Batch Processing/src/org/openconcerto/modules/common/batchprocessing/BooleanProcessor.java
13,13 → 13,13
import org.openconcerto.ui.VFlowLayout;
 
public class BooleanProcessor extends JPanel implements BatchProcessor {
private final SQLField field;
private final BatchField field;
 
private JRadioButton bTrue;
private JRadioButton bFalse;
private JRadioButton bInvert;
 
public BooleanProcessor(SQLField field) {
public BooleanProcessor(BatchField field) {
this.field = field;
this.setLayout(new VFlowLayout());
bTrue = new JRadioButton("forcer à vrai");
41,7 → 41,7
if (bTrue.isSelected()) {
for (SQLRowAccessor sqlRowAccessor : r) {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
rowValues.put(field.getName(), Boolean.TRUE);
rowValues.put(field.getField().getName(), Boolean.TRUE);
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
48,7 → 48,7
} else if (bFalse.isSelected()) {
for (SQLRowAccessor sqlRowAccessor : r) {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
rowValues.put(field.getName(), Boolean.FALSE);
rowValues.put(field.getField().getName(), Boolean.FALSE);
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
55,9 → 55,9
} else if (bInvert.isSelected()) {
for (SQLRowAccessor sqlRowAccessor : r) {
final SQLRowValues rowValues = sqlRowAccessor.createEmptyUpdateRow();
final Boolean boolean1 = sqlRowAccessor.asRow().getBoolean(field.getName());
final Boolean boolean1 = sqlRowAccessor.asRow().getBoolean(field.getField().getName());
if (boolean1 != null) {
rowValues.put(field.getName(), boolean1.equals(Boolean.FALSE));
rowValues.put(field.getField().getName(), boolean1.equals(Boolean.FALSE));
processBeforeUpdate(sqlRowAccessor, rowValues);
rowValues.update();
}
/trunk/Modules/Module Label/lib/barcode4j-2.1.0.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/lib/barcode4j-2.1.0.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/lib/jbarcode-0.2.8.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/lib/jbarcode-0.2.8.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/src/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/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/output/ExtendedOutputStreamWriter.java
New file
0,0 → 1,80
/*
* Copyright 2015 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
 
/**
* {@link OutputStreamWriter} extension which provides some convenience methods for writing numbers.
*/
class ExtendedOutputStreamWriter extends OutputStreamWriter {
 
/** Format to use when writing doubles to the stream. */
private final String doubleFormat;
 
/**
* Creates a new extended output stream writer, using the UTF-8 charset.
*
* @param out the stream to write to
* @param doubleFormat the format to use when writing doubles to the stream
*/
public ExtendedOutputStreamWriter(final OutputStream out, final String doubleFormat) {
super(out, StandardCharsets.UTF_8);
this.doubleFormat = doubleFormat;
}
 
/** {@inheritDoc} */
@Override
public ExtendedOutputStreamWriter append(final CharSequence cs) throws IOException {
super.append(cs);
return this;
}
 
/** {@inheritDoc} */
@Override
public ExtendedOutputStreamWriter append(final CharSequence cs, final int start, final int end) throws IOException {
super.append(cs, start, end);
return this;
}
 
/**
* Writes the specified double to the stream, formatted according to the format specified in the
* constructor.
*
* @param d the double to write to the stream
* @return this writer
* @throws IOException if an I/O error occurs
*/
public ExtendedOutputStreamWriter append(final double d) throws IOException {
super.append(String.format(Locale.ROOT, this.doubleFormat, d));
return this;
}
 
/**
* Writes the specified integer to the stream.
*
* @param i the integer to write to the stream
* @return this writer
* @throws IOException if an I/O error occurs
*/
public ExtendedOutputStreamWriter appendInt(final int i) throws IOException {
super.append(String.valueOf(i));
return this;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/SymbolRenderer.java
New file
0,0 → 1,34
/*
* Copyright 2015 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import java.io.IOException;
 
import uk.org.okapibarcode.backend.Symbol;
 
/**
* Renders symbols to some output format.
*/
public interface SymbolRenderer {
 
/**
* Renders the specified symbology.
*
* @param symbol the symbology to render
* @throws IOException if there is an I/O error
*/
void render(Symbol symbol) throws IOException;
 
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/Java2DRenderer.java
New file
0,0 → 1,169
/*
* Copyright 2014-2015 Robin Stuart, Robert Elliott, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER;
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY;
 
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.Collections;
import java.util.List;
 
import uk.org.okapibarcode.backend.Hexagon;
import uk.org.okapibarcode.backend.HumanReadableAlignment;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.backend.TextBox;
 
/**
* Renders symbologies using the Java 2D API.
*/
public class Java2DRenderer implements SymbolRenderer {
 
/** The graphics to render to. */
private final Graphics2D g2d;
 
/** The magnification factor to apply. */
private final double magnification;
 
/** The paper (background) color. */
private final Color paper;
 
/** The ink (foreground) color. */
private final Color ink;
 
/**
* Creates a new Java 2D renderer. If the specified paper color is <tt>null</tt>, the symbol is
* drawn without clearing the existing <tt>g2d</tt> background.
*
* @param g2d the graphics to render to
* @param magnification the magnification factor to apply
* @param paper the paper (background) color (may be <tt>null</tt>)
* @param ink the ink (foreground) color
*/
public Java2DRenderer(final Graphics2D g2d, final double magnification, final Color paper, final Color ink) {
this.g2d = g2d;
this.magnification = magnification;
this.paper = paper;
this.ink = ink;
}
 
/** {@inheritDoc} */
@Override
public void render(final Symbol symbol) {
 
final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification);
final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification);
 
Font f = symbol.getFont();
if (f != null) {
f = f.deriveFont((float) (f.getSize2D() * this.magnification));
} else {
f = new Font(symbol.getFontName(), Font.PLAIN, (int) (symbol.getFontSize() * this.magnification));
f = f.deriveFont(Collections.singletonMap(TextAttribute.TRACKING, 0));
}
 
final Font oldFont = this.g2d.getFont();
final Color oldColor = this.g2d.getColor();
 
if (this.paper != null) {
final int w = (int) (symbol.getWidth() * this.magnification);
final int h = (int) (symbol.getHeight() * this.magnification);
this.g2d.setColor(this.paper);
this.g2d.fillRect(0, 0, w, h);
}
 
this.g2d.setColor(this.ink);
 
for (final Rectangle2D.Double rect : symbol.getRectangles()) {
final double x = rect.x * this.magnification + marginX;
final double y = rect.y * this.magnification + marginY;
final double w = rect.width * this.magnification;
final double h = rect.height * this.magnification;
this.g2d.fillRect((int) x, (int) y, (int) w, (int) h);
}
 
for (final TextBox text : symbol.getTexts()) {
final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment;
final Font font = alignment != JUSTIFY ? f : addTracking(f, text.width * this.magnification, text.text, this.g2d);
this.g2d.setFont(font);
final FontMetrics fm = this.g2d.getFontMetrics();
final Rectangle2D bounds = fm.getStringBounds(text.text, this.g2d);
final float y = (float) (text.y * this.magnification) + marginY;
float x;
switch (alignment) {
case LEFT:
case JUSTIFY:
x = (float) (this.magnification * text.x + marginX);
break;
case RIGHT:
x = (float) (this.magnification * text.x + this.magnification * text.width - bounds.getWidth() + marginX);
break;
case CENTER:
x = (float) (this.magnification * text.x + this.magnification * text.width / 2 - bounds.getWidth() / 2 + marginX);
break;
default:
throw new IllegalStateException("Unknown alignment: " + alignment);
}
this.g2d.drawString(text.text, x, y);
}
 
for (final Hexagon hexagon : symbol.getHexagons()) {
final Polygon polygon = new Polygon();
for (int j = 0; j < 6; j++) {
polygon.addPoint((int) (hexagon.pointX[j] * this.magnification + marginX), (int) (hexagon.pointY[j] * this.magnification + marginY));
}
this.g2d.fill(polygon);
}
 
final List<Ellipse2D.Double> target = symbol.getTarget();
for (int i = 0; i + 1 < target.size(); i += 2) {
final Ellipse2D.Double outer = adjust(target.get(i), this.magnification, marginX, marginY);
final Ellipse2D.Double inner = adjust(target.get(i + 1), this.magnification, marginX, marginY);
final Area area = new Area(outer);
area.subtract(new Area(inner));
this.g2d.fill(area);
}
 
this.g2d.setFont(oldFont);
this.g2d.setColor(oldColor);
}
 
private static Ellipse2D.Double adjust(final Ellipse2D.Double ellipse, final double magnification, final int marginX, final int marginY) {
final double x = ellipse.x * magnification + marginX;
final double y = ellipse.y * magnification + marginY;
final double w = ellipse.width * magnification + marginX;
final double h = ellipse.height * magnification + marginY;
return new Ellipse2D.Double(x, y, w, h);
}
 
private static Font addTracking(final Font baseFont, final double maxTextWidth, final String text, final Graphics2D g2d) {
final FontRenderContext frc = g2d.getFontRenderContext();
final double originalWidth = baseFont.getStringBounds(text, frc).getWidth();
final double extraSpace = maxTextWidth - originalWidth;
final double extraSpacePerGap = extraSpace / (text.length() - 1);
final double scaleX = baseFont.isTransformed() ? baseFont.getTransform().getScaleX() : 1;
final double tracking = extraSpacePerGap / (baseFont.getSize2D() * scaleX);
return baseFont.deriveFont(Collections.singletonMap(TextAttribute.TRACKING, tracking));
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/PostScriptRenderer.java
New file
0,0 → 1,211
/*
* Copyright 2015 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER;
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY;
import static uk.org.okapibarcode.util.Doubles.roughlyEqual;
 
import java.awt.Color;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.OutputStream;
 
import uk.org.okapibarcode.backend.Hexagon;
import uk.org.okapibarcode.backend.HumanReadableAlignment;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.backend.TextBox;
 
/**
* Renders symbologies to EPS (Encapsulated PostScript).
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class PostScriptRenderer implements SymbolRenderer {
 
/** The output stream to render to. */
private final OutputStream out;
 
/** The magnification factor to apply. */
private final double magnification;
 
/** The paper (background) color. */
private final Color paper;
 
/** The ink (foreground) color. */
private final Color ink;
 
/**
* Creates a new PostScript renderer.
*
* @param out the output stream to render to
* @param magnification the magnification factor to apply
* @param paper the paper (background) color
* @param ink the ink (foreground) color
*/
public PostScriptRenderer(final OutputStream out, final double magnification, final Color paper, final Color ink) {
this.out = out;
this.magnification = magnification;
this.paper = paper;
this.ink = ink;
}
 
/** {@inheritDoc} */
@Override
public void render(final Symbol symbol) throws IOException {
 
// All y dimensions are reversed because EPS origin (0,0) is at the bottom left, not top
// left
 
final String content = symbol.getContent();
final int width = (int) (symbol.getWidth() * this.magnification);
final int height = (int) (symbol.getHeight() * this.magnification);
final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification);
final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification);
 
String title;
if (content == null || content.isEmpty()) {
title = "OkapiBarcode Generated Symbol";
} else {
title = content;
}
 
try (ExtendedOutputStreamWriter writer = new ExtendedOutputStreamWriter(this.out, "%.2f")) {
 
// Header
writer.append("%!PS-Adobe-3.0 EPSF-3.0\n");
writer.append("%%Creator: OkapiBarcode\n");
writer.append("%%Title: ").append(title).append('\n');
writer.append("%%Pages: 0\n");
writer.append("%%BoundingBox: 0 0 ").appendInt(width).append(" ").appendInt(height).append("\n");
writer.append("%%EndComments\n");
 
// Definitions
writer.append("/TL { setlinewidth moveto lineto stroke } bind def\n");
writer.append("/TC { moveto 0 360 arc 360 0 arcn fill } bind def\n");
writer.append("/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def\n");
writer.append("/TB { 2 copy } bind def\n");
writer.append("/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def\n");
writer.append("/TE { pop pop } bind def\n");
 
// Background
writer.append("newpath\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(this.paper.getRed() / 255.0).append(" ").append(this.paper.getGreen() / 255.0).append(" ").append(this.paper.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(height).append(" 0.00 TB 0.00 ").append(width).append(" TR\n");
 
// Rectangles
for (int i = 0; i < symbol.getRectangles().size(); i++) {
final Rectangle2D.Double rect = symbol.getRectangles().get(i);
if (i == 0) {
writer.append("TE\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(rect.height * this.magnification).append(" ").append(height - (rect.y + rect.height) * this.magnification - marginY).append(" TB ")
.append(rect.x * this.magnification + marginX).append(" ").append(rect.width * this.magnification).append(" TR\n");
} else {
final Rectangle2D.Double prev = symbol.getRectangles().get(i - 1);
if (!roughlyEqual(rect.height, prev.height) || !roughlyEqual(rect.y, prev.y)) {
writer.append("TE\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(rect.height * this.magnification).append(" ").append(height - (rect.y + rect.height) * this.magnification - marginY).append(" ");
}
writer.append("TB ").append(rect.x * this.magnification + marginX).append(" ").append(rect.width * this.magnification).append(" TR\n");
}
}
 
// Text
for (int i = 0; i < symbol.getTexts().size(); i++) {
final TextBox text = symbol.getTexts().get(i);
final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment;
if (i == 0) {
writer.append("TE\n");
;
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
}
writer.append("matrix currentmatrix\n");
writer.append("/").append(symbol.getFontName()).append(" findfont\n");
writer.append(symbol.getFontSize() * this.magnification).append(" scalefont setfont\n");
final double y = height - text.y * this.magnification - marginY;
switch (alignment) {
case LEFT:
final double leftX = this.magnification * text.x + marginX;
writer.append(" 0 0 moveto ").append(leftX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
writer.append(" (").append(text.text).append(") show\n");
break;
case JUSTIFY:
final double textX = this.magnification * text.x + marginX;
final double textW = this.magnification * text.width;
writer.append(" 0 0 moveto ").append(textX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
writer.append(" (").append(text.text).append(") dup stringwidth pop ").append(textW).append(" sub neg 1 index length 1 sub div 0").append(" 3 -1 roll ashow\n");
break;
case RIGHT:
final double rightX = this.magnification * text.x + this.magnification * text.width + marginX;
writer.append(" 0 0 moveto ").append(rightX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
writer.append(" (").append(text.text).append(") stringwidth\n");
writer.append("pop\n");
writer.append("-1 mul 0 rmoveto\n");
writer.append(" (").append(text.text).append(") show\n");
break;
case CENTER:
final double centerX = this.magnification * text.x + this.magnification * text.width / 2 + marginX;
writer.append(" 0 0 moveto ").append(centerX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
writer.append(" (").append(text.text).append(") stringwidth\n");
writer.append("pop\n");
writer.append("-2 div 0 rmoveto\n");
writer.append(" (").append(text.text).append(") show\n");
break;
default:
throw new IllegalStateException("Unknown alignment: " + alignment);
}
writer.append("setmatrix\n");
}
 
// Circles
// Because MaxiCode size is fixed, this ignores magnification
for (int i = 0; i < symbol.getTarget().size(); i += 2) {
final Ellipse2D.Double ellipse1 = symbol.getTarget().get(i);
final Ellipse2D.Double ellipse2 = symbol.getTarget().get(i + 1);
if (i == 0) {
writer.append("TE\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
}
final double x1 = ellipse1.x + ellipse1.width / 2;
final double x2 = ellipse2.x + ellipse2.width / 2;
final double y1 = height - ellipse1.y - ellipse1.width / 2;
final double y2 = height - ellipse2.y - ellipse2.width / 2;
final double r1 = ellipse1.width / 2;
final double r2 = ellipse2.width / 2;
writer.append(x1 + marginX).append(" ").append(y1 - marginY).append(" ").append(r1).append(" ").append(x2 + marginX).append(" ").append(y2 - marginY).append(" ").append(r2).append(" ")
.append(x2 + r2 + marginX).append(" ").append(y2 - marginY).append(" TC\n");
}
 
// Hexagons
// Because MaxiCode size is fixed, this ignores magnification
for (int i = 0; i < symbol.getHexagons().size(); i++) {
final Hexagon hexagon = symbol.getHexagons().get(i);
for (int j = 0; j < 6; j++) {
writer.append(hexagon.pointX[j] + marginX).append(" ").append(height - hexagon.pointY[j] - marginY).append(" ");
}
writer.append(" TH\n");
}
 
// Footer
writer.append("\nshowpage\n");
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/SvgRenderer.java
New file
0,0 → 1,225
/*
* Copyright 2014-2015 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER;
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY;
 
import java.awt.Color;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
 
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
 
import org.w3c.dom.Document;
import org.w3c.dom.Text;
 
import uk.org.okapibarcode.backend.Hexagon;
import uk.org.okapibarcode.backend.HumanReadableAlignment;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.backend.TextBox;
 
/**
* Renders symbologies to SVG (Scalable Vector Graphics).
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class SvgRenderer implements SymbolRenderer {
 
/** The output stream to render to. */
private final OutputStream out;
 
/** The magnification factor to apply. */
private final double magnification;
 
/** The paper (background) color. */
private final Color paper;
 
/** The ink (foreground) color. */
private final Color ink;
 
/** Whether or not to include the XML prolog in the output. */
private final boolean xmlProlog;
 
/**
* Creates a new SVG renderer.
*
* @param out the output stream to render to
* @param magnification the magnification factor to apply
* @param paper the paper (background) color
* @param ink the ink (foreground) color
* @param xmlProlog whether or not to include the XML prolog in the output (usually {@code true}
* for standalone SVG documents, {@code false} for SVG content embedded directly in HTML
* documents)
*/
public SvgRenderer(final OutputStream out, final double magnification, final Color paper, final Color ink, final boolean xmlProlog) {
this.out = out;
this.magnification = magnification;
this.paper = paper;
this.ink = ink;
this.xmlProlog = xmlProlog;
}
 
/** {@inheritDoc} */
@Override
public void render(final Symbol symbol) throws IOException {
 
final String content = symbol.getContent();
final int width = (int) (symbol.getWidth() * this.magnification);
final int height = (int) (symbol.getHeight() * this.magnification);
final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification);
final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification);
 
String title;
if (content == null || content.isEmpty()) {
title = "OkapiBarcode Generated Symbol";
} else {
title = content;
}
 
final String fgColour = String.format("%02X", this.ink.getRed()) + String.format("%02X", this.ink.getGreen()) + String.format("%02X", this.ink.getBlue());
 
final String bgColour = String.format("%02X", this.paper.getRed()) + String.format("%02X", this.paper.getGreen()) + String.format("%02X", this.paper.getBlue());
 
try (ExtendedOutputStreamWriter writer = new ExtendedOutputStreamWriter(this.out, "%.2f")) {
 
// XML Prolog
if (this.xmlProlog) {
writer.append("<?xml version=\"1.0\" standalone=\"no\"?>\n");
writer.append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n");
writer.append(" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
}
 
// Header
writer.append("<svg width=\"").appendInt(width).append("\" height=\"").appendInt(height).append("\" version=\"1.1").append("\" xmlns=\"http://www.w3.org/2000/svg\">\n");
writer.append(" <desc>").append(clean(title)).append("</desc>\n");
writer.append(" <g id=\"barcode\" fill=\"#").append(fgColour).append("\">\n");
writer.append(" <rect x=\"0\" y=\"0\" width=\"").appendInt(width).append("\" height=\"").appendInt(height).append("\" fill=\"#").append(bgColour).append("\" />\n");
 
// Rectangles
for (int i = 0; i < symbol.getRectangles().size(); i++) {
final Rectangle2D.Double rect = symbol.getRectangles().get(i);
writer.append(" <rect x=\"").append(rect.x * this.magnification + marginX).append("\" y=\"").append(rect.y * this.magnification + marginY).append("\" width=\"")
.append(rect.width * this.magnification).append("\" height=\"").append(rect.height * this.magnification).append("\" />\n");
}
 
// Text
for (int i = 0; i < symbol.getTexts().size(); i++) {
final TextBox text = symbol.getTexts().get(i);
final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment;
double x;
String anchor;
switch (alignment) {
case LEFT:
case JUSTIFY:
x = this.magnification * text.x + marginX;
anchor = "start";
break;
case RIGHT:
x = this.magnification * text.x + this.magnification * text.width + marginX;
anchor = "end";
break;
case CENTER:
x = this.magnification * text.x + this.magnification * text.width / 2 + marginX;
anchor = "middle";
break;
default:
throw new IllegalStateException("Unknown alignment: " + alignment);
}
writer.append(" <text x=\"").append(x).append("\" y=\"").append(text.y * this.magnification + marginY).append("\" text-anchor=\"").append(anchor).append("\"\n");
if (alignment == JUSTIFY) {
writer.append(" textLength=\"").append(text.width * this.magnification).append("\" lengthAdjust=\"spacing\"\n");
}
writer.append(" font-family=\"").append(clean(symbol.getFontName())).append("\" font-size=\"").append(symbol.getFontSize() * this.magnification).append("\" fill=\"#")
.append(fgColour).append("\">\n");
writer.append(" ").append(clean(text.text)).append("\n");
writer.append(" </text>\n");
}
 
// Circles
for (int i = 0; i < symbol.getTarget().size(); i++) {
final Ellipse2D.Double ellipse = symbol.getTarget().get(i);
String color;
if ((i & 1) == 0) {
color = fgColour;
} else {
color = bgColour;
}
writer.append(" <circle cx=\"").append((ellipse.x + ellipse.width / 2) * this.magnification + marginX).append("\" cy=\"")
.append((ellipse.y + ellipse.width / 2) * this.magnification + marginY).append("\" r=\"").append(ellipse.width / 2 * this.magnification).append("\" fill=\"#").append(color)
.append("\" />\n");
}
 
// Hexagons
for (int i = 0; i < symbol.getHexagons().size(); i++) {
final Hexagon hexagon = symbol.getHexagons().get(i);
writer.append(" <path d=\"");
for (int j = 0; j < 6; j++) {
if (j == 0) {
writer.append("M ");
} else {
writer.append("L ");
}
writer.append(hexagon.pointX[j] * this.magnification + marginX).append(" ").append(hexagon.pointY[j] * this.magnification + marginY).append(" ");
}
writer.append("Z\" />\n");
}
 
// Footer
writer.append(" </g>\n");
writer.append("</svg>\n");
}
}
 
/**
* Cleans / sanitizes the specified string for inclusion in XML. A bit convoluted, but we're
* trying to do it without adding an external dependency just for this...
*
* @param s the string to be cleaned / sanitized
* @return the cleaned / sanitized string
*/
protected String clean(String s) {
 
// remove control characters
s = s.replaceAll("[\u0000-\u001f]", "");
 
// escape XML characters
try {
final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
final Text text = document.createTextNode(s);
final Transformer transformer = TransformerFactory.newInstance().newTransformer();
final DOMSource source = new DOMSource(text);
final StringWriter writer = new StringWriter();
final StreamResult result = new StreamResult(writer);
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(source, result);
return writer.toString();
} catch (ParserConfigurationException | TransformerException | TransformerFactoryConfigurationError e) {
return s;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code11.java
New file
0,0 → 1,215
/*
* Copyright 2014 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
/**
* <p>
* Implements Code 11 bar code symbology.
*
* <p>
* Code 11 can encode any length string consisting of the digits 0-9 and the dash character (-). One
* or two modulo-11 check digits are calculated.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class Code11 extends Symbol {
 
private static final String[] CODE_11_TABLE = { "111121", "211121", "121121", "221111", "112121", "212111", "122111", "111221", "211211", "211111", "112111" };
 
private static final char[] CHARACTER_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' };
 
/** Ratio of wide bar width to narrow bar width. */
private double moduleWidthRatio = 2;
 
/** The number of check digits to calculate ({@code 1} or {@code 2}). */
private int checkDigitCount = 2;
 
/** Optional start delimiter to be shown in the human-readable text. */
private Character startDelimiter;
 
/** Optional stop delimiter to be shown in the human-readable text. */
private Character stopDelimiter;
 
/**
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually between
* {@code 2} and {@code 3}. The default value is {@code 2}.
*
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width
*/
public void setModuleWidthRatio(final double moduleWidthRatio) {
this.moduleWidthRatio = moduleWidthRatio;
}
 
/**
* Returns the ratio of wide bar width to narrow bar width.
*
* @return the ratio of wide bar width to narrow bar width
*/
public double getModuleWidthRatio() {
return this.moduleWidthRatio;
}
 
/**
* Sets the number of check digits to calculate ({@code 1} or {@code 2}). The default value is
* {@code 2}.
*
* @param checkDigitCount the number of check digits to calculate
*/
public void setCheckDigitCount(final int checkDigitCount) {
if (checkDigitCount < 1 || checkDigitCount > 2) {
throw new IllegalArgumentException("Check digit count must be 1 or 2.");
}
this.checkDigitCount = checkDigitCount;
}
 
/**
* Returns the number of check digits to calculate (1 or 2).
*
* @return the number of check digits to calculate
*/
public int getCheckDigitCount() {
return this.checkDigitCount;
}
 
/**
* Sets an optional start delimiter to be shown in the human-readable text (defaults to
* <code>null</code>).
*
* @param startDelimiter an optional start delimiter to be shown in the human-readable text
*/
public void setStartDelimiter(final Character startDelimiter) {
this.startDelimiter = startDelimiter;
}
 
/**
* Returns the optional start delimiter to be shown in the human-readable text.
*
* @return the optional start delimiter to be shown in the human-readable text
*/
public Character getStartDelimiter() {
return this.startDelimiter;
}
 
/**
* Sets an optional stop delimiter to be shown in the human-readable text (defaults to
* <code>null</code>).
*
* @param stopDelimiter an optional stop delimiter to be shown in the human-readable text
*/
public void setStopDelimiter(final Character stopDelimiter) {
this.stopDelimiter = stopDelimiter;
}
 
/**
* Returns the optional stop delimiter to be shown in the human-readable text.
*
* @return the optional stop delimiter to be shown in the human-readable text
*/
public Character getStopDelimiter() {
return this.stopDelimiter;
}
 
/** {@inheritDoc} */
@Override
protected void encode() {
 
if (!this.content.matches("[0-9-]+")) {
throw new OkapiException("Invalid characters in input");
}
 
String horizontalSpacing = "112211";
String humanReadable = this.content;
final int length = this.content.length();
final int[] weight = new int[length + 1];
 
for (int i = 0; i < length; i++) {
final char c = this.content.charAt(i);
weight[i] = positionOf(c, CHARACTER_SET);
horizontalSpacing += CODE_11_TABLE[weight[i]];
}
 
final int checkDigitC = getCheckDigitC(weight, length);
horizontalSpacing += CODE_11_TABLE[checkDigitC];
humanReadable += CHARACTER_SET[checkDigitC];
infoLine("Check Digit C: " + checkDigitC);
 
if (this.checkDigitCount == 2) {
weight[length] = checkDigitC;
final int checkDigitK = getCheckDigitK(weight, length + 1);
horizontalSpacing += CODE_11_TABLE[checkDigitK];
humanReadable += CHARACTER_SET[checkDigitK];
infoLine("Check Digit K: " + checkDigitK);
}
 
horizontalSpacing += "112211";
 
this.readable = humanReadable;
if (this.startDelimiter != null) {
this.readable = this.startDelimiter + this.readable;
}
if (this.stopDelimiter != null) {
this.readable = this.readable + this.stopDelimiter;
}
 
this.pattern = new String[] { horizontalSpacing };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private static int getCheckDigitC(final int[] weight, final int length) {
int countC = 0;
int weightC = 1;
for (int i = length - 1; i >= 0; i--) {
countC += weightC * weight[i];
weightC++;
if (weightC > 10) {
weightC = 1;
}
}
return countC % 11;
}
 
private static int getCheckDigitK(final int[] weight, final int length) {
int countK = 0;
int weightK = 1;
for (int i = length - 1; i >= 0; i--) {
countK += weightK * weight[i];
weightK++;
if (weightK > 9) {
weightK = 1;
}
}
return countK % 11;
}
 
/** {@inheritDoc} */
@Override
protected double getModuleWidth(final int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return this.moduleWidthRatio;
}
}
 
/** {@inheritDoc} */
@Override
protected int[] getCodewords() {
return getPatternAsCodewords(6);
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/QrCode.java
New file
0,0 → 1,1692
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
import java.nio.CharBuffer;
import java.nio.charset.Charset;
 
/**
* <p>
* Implements QR Code bar code symbology According to ISO/IEC 18004:2015.
*
* <p>
* The maximum capacity of a (version 40) QR Code symbol is 7089 numeric digits, 4296 alphanumeric
* characters or 2953 bytes of data. QR Code symbols can also be used to encode GS1 data. QR Code
* symbols can encode characters in the Latin-1 set and Kanji characters which are members of the
* Shift-JIS encoding scheme.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class QrCode extends Symbol {
 
public enum EccLevel {
L, M, Q, H
}
 
private enum QrMode {
NULL, KANJI, BINARY, ALPHANUM, NUMERIC
}
 
/* Table 5 - Encoding/Decoding table for Alphanumeric mode */
private static final char[] RHODIUM = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' };
 
private static final int[] QR_DATA_CODEWORDS_L = { 19, 34, 55, 80, 108, 136, 156, 194, 232, 274, 324, 370, 428, 461, 523, 589, 647, 721, 795, 861, 932, 1006, 1094, 1174, 1276, 1370, 1468, 1531,
1631, 1735, 1843, 1955, 2071, 2191, 2306, 2434, 2566, 2702, 2812, 2956 };
 
private static final int[] QR_DATA_CODEWORDS_M = { 16, 28, 44, 64, 86, 108, 124, 154, 182, 216, 254, 290, 334, 365, 415, 453, 507, 563, 627, 669, 714, 782, 860, 914, 1000, 1062, 1128, 1193, 1267,
1373, 1455, 1541, 1631, 1725, 1812, 1914, 1992, 2102, 2216, 2334 };
 
private static final int[] QR_DATA_CODEWORDS_Q = { 13, 22, 34, 48, 62, 76, 88, 110, 132, 154, 180, 206, 244, 261, 295, 325, 367, 397, 445, 485, 512, 568, 614, 664, 718, 754, 808, 871, 911, 985,
1033, 1115, 1171, 1231, 1286, 1354, 1426, 1502, 1582, 1666 };
 
private static final int[] QR_DATA_CODEWORDS_H = { 9, 16, 26, 36, 46, 60, 66, 86, 100, 122, 140, 158, 180, 197, 223, 253, 283, 313, 341, 385, 406, 442, 464, 514, 538, 596, 628, 661, 701, 745, 793,
845, 901, 961, 986, 1054, 1096, 1142, 1222, 1276 };
 
private static final int[] QR_BLOCKS_L = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25 };
 
private static final int[] QR_BLOCKS_M = { 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49 };
 
private static final int[] QR_BLOCKS_Q = { 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68 };
 
private static final int[] QR_BLOCKS_H = { 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81 };
 
private static final int[] QR_TOTAL_CODEWORDS = { 26, 44, 70, 100, 134, 172, 196, 242, 292, 346, 404, 466, 532, 581, 655, 733, 815, 901, 991, 1085, 1156, 1258, 1364, 1474, 1588, 1706, 1828, 1921,
2051, 2185, 2323, 2465, 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706 };
 
private static final int[] QR_SIZES = { 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157,
161, 165, 169, 173, 177 };
 
private static final int[] QR_ALIGN_LOOPSIZE = { 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7 };
 
private static final int[] QR_TABLE_E1 = { 6, 18, 0, 0, 0, 0, 0, 6, 22, 0, 0, 0, 0, 0, 6, 26, 0, 0, 0, 0, 0, 6, 30, 0, 0, 0, 0, 0, 6, 34, 0, 0, 0, 0, 0, 6, 22, 38, 0, 0, 0, 0, 6, 24, 42, 0, 0, 0,
0, 6, 26, 46, 0, 0, 0, 0, 6, 28, 50, 0, 0, 0, 0, 6, 30, 54, 0, 0, 0, 0, 6, 32, 58, 0, 0, 0, 0, 6, 34, 62, 0, 0, 0, 0, 6, 26, 46, 66, 0, 0, 0, 6, 26, 48, 70, 0, 0, 0, 6, 26, 50, 74, 0, 0,
0, 6, 30, 54, 78, 0, 0, 0, 6, 30, 56, 82, 0, 0, 0, 6, 30, 58, 86, 0, 0, 0, 6, 34, 62, 90, 0, 0, 0, 6, 28, 50, 72, 94, 0, 0, 6, 26, 50, 74, 98, 0, 0, 6, 30, 54, 78, 102, 0, 0, 6, 28, 54,
80, 106, 0, 0, 6, 32, 58, 84, 110, 0, 0, 6, 30, 58, 86, 114, 0, 0, 6, 34, 62, 90, 118, 0, 0, 6, 26, 50, 74, 98, 122, 0, 6, 30, 54, 78, 102, 126, 0, 6, 26, 52, 78, 104, 130, 0, 6, 30, 56,
82, 108, 134, 0, 6, 34, 60, 86, 112, 138, 0, 6, 30, 58, 86, 114, 142, 0, 6, 34, 62, 90, 118, 146, 0, 6, 30, 54, 78, 102, 126, 150, 6, 24, 50, 76, 102, 128, 154, 6, 28, 54, 80, 106, 132,
158, 6, 32, 58, 84, 110, 136, 162, 6, 26, 54, 82, 110, 138, 166, 6, 30, 58, 86, 114, 142, 170 };
 
private static final int[] QR_ANNEX_C = {
/* Format information bit sequences */
0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0, 0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976, 0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c,
0x083b, 0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed };
 
private static final int[] QR_ANNEX_D = {
/* Version information bit sequences */
0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e,
0x1cc1a, 0x1d33f, 0x1ed75, 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, 0x27541, 0x28c69 };
 
private int preferredVersion;
private EccLevel preferredEccLevel = EccLevel.L;
 
/**
* Sets the preferred symbol size / version. This value may be ignored if the data string is too
* large to fit into the specified symbol. Input values correspond to symbol sizes as shown in
* the following table:
*
* <table summary="Available QR Code sizes">
* <tbody>
* <tr>
* <th>Input</th>
* <th>Symbol Size</th>
* <th>Input</th>
* <th>Symbol Size</th>
* </tr>
* <tr>
* <td>1</td>
* <td>21 x 21</td>
* <td>21</td>
* <td>101 x 101</td>
* </tr>
* <tr>
* <td>2</td>
* <td>25 x 25</td>
* <td>22</td>
* <td>105 x 105</td>
* </tr>
* <tr>
* <td>3</td>
* <td>29 x 29</td>
* <td>23</td>
* <td>109 x 109</td>
* </tr>
* <tr>
* <td>4</td>
* <td>33 x 33</td>
* <td>24</td>
* <td>113 x 113</td>
* </tr>
* <tr>
* <td>5</td>
* <td>37 x 37</td>
* <td>25</td>
* <td>117 x 117</td>
* </tr>
* <tr>
* <td>6</td>
* <td>41 x 41</td>
* <td>26</td>
* <td>121 x 121</td>
* </tr>
* <tr>
* <td>7</td>
* <td>45 x 45</td>
* <td>27</td>
* <td>125 x 125</td>
* </tr>
* <tr>
* <td>8</td>
* <td>49 x 49</td>
* <td>28</td>
* <td>129 x 129</td>
* </tr>
* <tr>
* <td>9</td>
* <td>53 x 53</td>
* <td>29</td>
* <td>133 x 133</td>
* </tr>
* <tr>
* <td>10</td>
* <td>57 x 57</td>
* <td>30</td>
* <td>137 x 137</td>
* </tr>
* <tr>
* <td>11</td>
* <td>61 x 61</td>
* <td>31</td>
* <td>141 x 141</td>
* </tr>
* <tr>
* <td>12</td>
* <td>65 x 65</td>
* <td>32</td>
* <td>145 x 145</td>
* </tr>
* <tr>
* <td>13</td>
* <td>69 x 69</td>
* <td>33</td>
* <td>149 x 149</td>
* </tr>
* <tr>
* <td>14</td>
* <td>73 x 73</td>
* <td>34</td>
* <td>153 x 153</td>
* </tr>
* <tr>
* <td>15</td>
* <td>77 x 77</td>
* <td>35</td>
* <td>157 x 157</td>
* </tr>
* <tr>
* <td>16</td>
* <td>81 x 81</td>
* <td>36</td>
* <td>161 x 161</td>
* </tr>
* <tr>
* <td>17</td>
* <td>85 x 85</td>
* <td>37</td>
* <td>165 x 165</td>
* </tr>
* <tr>
* <td>18</td>
* <td>89 x 89</td>
* <td>38</td>
* <td>169 x 169</td>
* </tr>
* <tr>
* <td>19</td>
* <td>93 x 93</td>
* <td>39</td>
* <td>173 x 173</td>
* </tr>
* <tr>
* <td>20</td>
* <td>97 x 97</td>
* <td>40</td>
* <td>177 x 177</td>
* </tr>
* </tbody>
* </table>
*
* @param version the preferred symbol version
*/
public void setPreferredVersion(final int version) {
this.preferredVersion = version;
}
 
/**
* Returns the preferred symbol version.
*
* @return the preferred symbol version
*/
public int getPreferredVersion() {
return this.preferredVersion;
}
 
/**
* Sets the preferred amount of symbol space allocated to error correction. This value may be
* ignored if there is room for a higher error correction level. Levels are predefined according
* to the following table:
*
* <table summary="QR Code error correction levels">
* <tbody>
* <tr>
* <th>ECC Level</th>
* <th>Error Correction Capacity</th>
* <th>Recovery Capacity</th>
* </tr>
* <tr>
* <td>L (default)</td>
* <td>Approx 20% of symbol</td>
* <td>Approx 7%</td>
* </tr>
* <tr>
* <td>M</td>
* <td>Approx 37% of symbol</td>
* <td>Approx 15%</td>
* </tr>
* <tr>
* <td>Q</td>
* <td>Approx 55% of symbol</td>
* <td>Approx 25%</td>
* </tr>
* <tr>
* <td>H</td>
* <td>Approx 65% of symbol</td>
* <td>Approx 30%</td>
* </tr>
* </tbody>
* </table>
*
* @param preferredEccLevel the preferred error correction level
*/
public void setPreferredEccLevel(final EccLevel preferredEccLevel) {
this.preferredEccLevel = preferredEccLevel;
}
 
/**
* Returns the preferred amount of symbol space allocated to error correction.
*
* @return the preferred amount of symbol space allocated to error correction
*/
public EccLevel getPreferredEccLevel() {
return this.preferredEccLevel;
}
 
@Override
protected boolean gs1Supported() {
return true;
}
 
@Override
protected void encode() {
 
int i, j;
int est_binlen;
EccLevel ecc_level;
int max_cw;
int targetCwCount, version, blocks;
int size;
int bitmask;
final boolean gs1 = this.inputDataType == DataType.GS1;
 
eciProcess(); // Get ECI mode
 
if (this.eciMode == 20) {
/* Shift-JIS encoding, use Kanji mode */
final Charset c = Charset.forName("Shift_JIS");
this.inputData = new int[this.content.length()];
for (i = 0; i < this.inputData.length; i++) {
final CharBuffer buffer = CharBuffer.wrap(this.content, i, i + 1);
final byte[] bytes = c.encode(buffer).array();
final int value = bytes.length == 2 ? (bytes[0] & 0xff) << 8 | bytes[1] & 0xff : bytes[0];
this.inputData[i] = value;
}
} else {
/* inputData already initialized in eciProcess() */
}
 
QrMode[] inputMode = new QrMode[this.inputData.length];
defineMode(inputMode, this.inputData);
est_binlen = getBinaryLength(40, inputMode, this.inputData, gs1, this.eciMode);
 
ecc_level = this.preferredEccLevel;
switch (this.preferredEccLevel) {
case L:
default:
max_cw = 2956;
break;
case M:
max_cw = 2334;
break;
case Q:
max_cw = 1666;
break;
case H:
max_cw = 1276;
break;
}
 
if (est_binlen > 8 * max_cw) {
throw new OkapiException("Input too long for selected error correction level");
}
 
// ZINT NOTE: this block is different from the corresponding block of code in Zint;
// it is simplified, but the simplification required that the applyOptimisation method
// be changed to be free of side effects (by putting the optimized mode array into a
// new array instead of modifying the existing array)
 
version = 40;
for (i = 39; i >= 0; i--) {
int[] dataCodewords;
switch (ecc_level) {
case L:
default:
dataCodewords = QR_DATA_CODEWORDS_L;
break;
case M:
dataCodewords = QR_DATA_CODEWORDS_M;
break;
case Q:
dataCodewords = QR_DATA_CODEWORDS_Q;
break;
case H:
dataCodewords = QR_DATA_CODEWORDS_H;
break;
}
final int proposedVersion = i + 1;
final int proposedBinLen = getBinaryLength(proposedVersion, inputMode, this.inputData, gs1, this.eciMode);
if (8 * dataCodewords[i] >= proposedBinLen) {
version = proposedVersion;
est_binlen = proposedBinLen;
}
}
 
inputMode = applyOptimisation(version, inputMode);
 
// ZINT NOTE: end of block of code that is different
 
// TODO: delete this
//
// autosize = 40;
// for (i = 39; i >= 0; i--) {
// switch (ecc_level) {
// case L:
// if ((8 * QR_DATA_CODEWORDS_L[i]) >= est_binlen) {
// autosize = i + 1;
// }
// break;
// case M:
// if ((8 * QR_DATA_CODEWORDS_M[i]) >= est_binlen) {
// autosize = i + 1;
// }
// break;
// case Q:
// if ((8 * QR_DATA_CODEWORDS_Q[i]) >= est_binlen) {
// autosize = i + 1;
// }
// break;
// case H:
// if ((8 * QR_DATA_CODEWORDS_H[i]) >= est_binlen) {
// autosize = i + 1;
// }
// break;
// }
// }
//
// // Now see if the optimized binary will fit in a smaller symbol.
// canShrink = true;
//
// do {
// if (autosize == 1) {
// est_binlen = getBinaryLength(autosize, inputMode, inputData, gs1, eciMode); // TODO:
// added
// canShrink = false;
// } else {
// est_binlen = getBinaryLength(autosize - 1, inputMode, inputData, gs1, eciMode);
//
// switch (ecc_level) {
// case L:
// if ((8 * QR_DATA_CODEWORDS_L[autosize - 2]) < est_binlen) {
// canShrink = false;
// }
// break;
// case M:
// if ((8 * QR_DATA_CODEWORDS_M[autosize - 2]) < est_binlen) {
// canShrink = false;
// }
// break;
// case Q:
// if ((8 * QR_DATA_CODEWORDS_Q[autosize - 2]) < est_binlen) {
// canShrink = false;
// }
// break;
// case H:
// if ((8 * QR_DATA_CODEWORDS_H[autosize - 2]) < est_binlen) {
// canShrink = false;
// }
// break;
// }
//
// if (canShrink) {
// // Optimization worked - data will fit in a smaller symbol
// autosize--;
// } else {
// // Data did not fit in the smaller symbol, revert to original size
// est_binlen = getBinaryLength(autosize, inputMode, inputData, gs1, eciMode);
// }
// }
// } while (canShrink);
//
// version = autosize;
 
if (this.preferredVersion >= 1 && this.preferredVersion <= 40) {
/*
* If the user has selected a larger symbol than the smallest available, then use the
* size the user has selected, and re-optimize for this symbol size.
*/
if (this.preferredVersion > version) {
version = this.preferredVersion;
est_binlen = getBinaryLength(this.preferredVersion, inputMode, this.inputData, gs1, this.eciMode);
inputMode = applyOptimisation(version, inputMode);
}
if (this.preferredVersion < version) {
throw new OkapiException("Input too long for selected symbol size");
}
}
 
/* Ensure maximum error correction capacity */
if (est_binlen <= QR_DATA_CODEWORDS_M[version - 1] * 8) {
ecc_level = EccLevel.M;
}
if (est_binlen <= QR_DATA_CODEWORDS_Q[version - 1] * 8) {
ecc_level = EccLevel.Q;
}
if (est_binlen <= QR_DATA_CODEWORDS_H[version - 1] * 8) {
ecc_level = EccLevel.H;
}
 
targetCwCount = QR_DATA_CODEWORDS_L[version - 1];
blocks = QR_BLOCKS_L[version - 1];
switch (ecc_level) {
case M:
targetCwCount = QR_DATA_CODEWORDS_M[version - 1];
blocks = QR_BLOCKS_M[version - 1];
break;
case Q:
targetCwCount = QR_DATA_CODEWORDS_Q[version - 1];
blocks = QR_BLOCKS_Q[version - 1];
break;
case H:
targetCwCount = QR_DATA_CODEWORDS_H[version - 1];
blocks = QR_BLOCKS_H[version - 1];
break;
}
 
final int[] datastream = new int[targetCwCount + 1];
final int[] fullstream = new int[QR_TOTAL_CODEWORDS[version - 1] + 1];
 
qrBinary(datastream, version, targetCwCount, inputMode, this.inputData, gs1, this.eciMode, est_binlen);
addEcc(fullstream, datastream, version, targetCwCount, blocks);
 
size = QR_SIZES[version - 1];
 
final int[] grid = new int[size * size];
 
infoLine("Version: " + version);
infoLine("ECC Level: " + ecc_level.name());
 
setupGrid(grid, size, version);
populateGrid(grid, size, fullstream, QR_TOTAL_CODEWORDS[version - 1]);
 
if (version >= 7) {
addVersionInfo(grid, size, version);
}
 
bitmask = applyBitmask(grid, size, ecc_level);
infoLine("Mask Pattern: " + Integer.toBinaryString(bitmask));
addFormatInfo(grid, size, ecc_level, bitmask);
 
this.readable = "";
this.pattern = new String[size];
this.row_count = size;
this.row_height = new int[size];
for (i = 0; i < size; i++) {
final StringBuilder bin = new StringBuilder(size);
for (j = 0; j < size; j++) {
if ((grid[i * size + j] & 0x01) != 0) {
bin.append('1');
} else {
bin.append('0');
}
}
this.pattern[i] = bin2pat(bin);
this.row_height[i] = 1;
}
}
 
/** Place Kanji / Binary / Alphanumeric / Numeric values in inputMode. */
private static void defineMode(final QrMode[] inputMode, final int[] inputData) {
 
for (int i = 0; i < inputData.length; i++) {
if (inputData[i] > 0xff) {
inputMode[i] = QrMode.KANJI;
} else {
inputMode[i] = QrMode.BINARY;
if (isAlpha(inputData[i])) {
inputMode[i] = QrMode.ALPHANUM;
}
if (inputData[i] == FNC1) {
inputMode[i] = QrMode.ALPHANUM;
}
if (isNumeric(inputData[i])) {
inputMode[i] = QrMode.NUMERIC;
}
}
}
 
// TODO: uncomment
// /* If less than 6 numeric digits together then don't use numeric mode */
// for (int i = 0; i < inputMode.length; i++) {
// if (inputMode[i] == QrMode.NUMERIC) {
// if (((i != 0) && (inputMode[i - 1] != QrMode.NUMERIC)) || (i == 0)) {
// mlen = 0;
// while (((mlen + i) < inputMode.length) && (inputMode[mlen + i] == QrMode.NUMERIC)) {
// mlen++;
// };
// if (mlen < 6) {
// for (int j = 0; j < mlen; j++) {
// inputMode[i + j] = QrMode.ALPHANUM;
// }
// }
// }
// }
// }
//
// /* If less than 4 alphanumeric characters together then don't use alphanumeric mode */
// for (int i = 0; i < inputMode.length; i++) {
// if (inputMode[i] == QrMode.ALPHANUM) {
// if (((i != 0) && (inputMode[i - 1] != QrMode.ALPHANUM)) || (i == 0)) {
// mlen = 0;
// while (((mlen + i) < inputMode.length) && (inputMode[mlen + i] == QrMode.ALPHANUM)) {
// mlen++;
// };
// if (mlen < 4) {
// for (int j = 0; j < mlen; j++) {
// inputMode[i + j] = QrMode.BINARY;
// }
// }
// }
// }
// }
}
 
/** Calculate the actual bit length of the proposed binary string. */
private static int getBinaryLength(final int version, final QrMode[] inputModeUnoptimized, final int[] inputData, final boolean gs1, final int eciMode) {
 
int i, j;
QrMode currentMode;
final int inputLength = inputModeUnoptimized.length;
int count = 0;
int alphaLength;
int percent = 0;
 
// ZINT NOTE: in Zint, this call modifies the input mode array directly; here, we leave
// the original array alone so that subsequent binary length checks don't irrevocably
// optimize the mode array for the wrong QR Code version
final QrMode[] inputMode = applyOptimisation(version, inputModeUnoptimized);
 
currentMode = QrMode.NULL;
 
if (gs1) {
count += 4;
}
 
if (eciMode != 3) {
count += 12;
}
 
for (i = 0; i < inputLength; i++) {
if (inputMode[i] != currentMode) {
count += 4;
switch (inputMode[i]) {
case KANJI:
count += tribus(version, 8, 10, 12);
count += blockLength(i, inputMode) * 13;
break;
case BINARY:
count += tribus(version, 8, 16, 16);
for (j = i; j < i + blockLength(i, inputMode); j++) {
if (inputData[j] > 0xff) {
count += 16;
} else {
count += 8;
}
}
break;
case ALPHANUM:
count += tribus(version, 9, 11, 13);
alphaLength = blockLength(i, inputMode);
// In alphanumeric mode % becomes %%
if (gs1) {
for (j = i; j < i + alphaLength; j++) { // TODO: need to do this only if
// in GS1 mode? or is the other
// code wrong?
// https://sourceforge.net/p/zint/tickets/104/#227b
if (inputData[j] == '%') {
percent++;
}
}
}
alphaLength += percent;
switch (alphaLength % 2) {
case 0:
count += alphaLength / 2 * 11;
break;
case 1:
count += (alphaLength - 1) / 2 * 11;
count += 6;
break;
}
break;
case NUMERIC:
count += tribus(version, 10, 12, 14);
switch (blockLength(i, inputMode) % 3) {
case 0:
count += blockLength(i, inputMode) / 3 * 10;
break;
case 1:
count += (blockLength(i, inputMode) - 1) / 3 * 10;
count += 4;
break;
case 2:
count += (blockLength(i, inputMode) - 2) / 3 * 10;
count += 7;
break;
}
break;
}
currentMode = inputMode[i];
}
}
 
return count;
}
 
/**
* Implements a custom optimization algorithm, because implementation of the algorithm shown in
* Annex J.2 created LONGER binary sequences.
*/
private static QrMode[] applyOptimisation(final int version, final QrMode[] inputMode) {
 
final int inputLength = inputMode.length;
int blockCount = 0;
int i, j;
QrMode currentMode = QrMode.NULL;
 
for (i = 0; i < inputLength; i++) {
if (inputMode[i] != currentMode) {
currentMode = inputMode[i];
blockCount++;
}
}
 
final int[] blockLength = new int[blockCount];
final QrMode[] blockMode = new QrMode[blockCount];
 
j = -1;
currentMode = QrMode.NULL;
for (i = 0; i < inputLength; i++) {
if (inputMode[i] != currentMode) {
j++;
blockLength[j] = 1;
blockMode[j] = inputMode[i];
currentMode = inputMode[i];
} else {
blockLength[j]++;
}
}
 
if (blockCount > 1) {
// Search forward
for (i = 0; i <= blockCount - 2; i++) {
if (blockMode[i] == QrMode.BINARY) {
switch (blockMode[i + 1]) {
case KANJI:
if (blockLength[i + 1] < tribus(version, 4, 5, 6)) {
blockMode[i + 1] = QrMode.BINARY;
}
break;
case ALPHANUM:
if (blockLength[i + 1] < tribus(version, 7, 8, 9)) {
blockMode[i + 1] = QrMode.BINARY;
}
break;
case NUMERIC:
if (blockLength[i + 1] < tribus(version, 3, 4, 5)) {
blockMode[i + 1] = QrMode.BINARY;
}
break;
}
}
 
if (blockMode[i] == QrMode.ALPHANUM && blockMode[i + 1] == QrMode.NUMERIC) {
if (blockLength[i + 1] < tribus(version, 6, 8, 10)) {
blockMode[i + 1] = QrMode.ALPHANUM;
}
}
}
 
// Search backward
for (i = blockCount - 1; i > 0; i--) {
if (blockMode[i] == QrMode.BINARY) {
switch (blockMode[i - 1]) {
case KANJI:
if (blockLength[i - 1] < tribus(version, 4, 5, 6)) {
blockMode[i - 1] = QrMode.BINARY;
}
break;
case ALPHANUM:
if (blockLength[i - 1] < tribus(version, 7, 8, 9)) {
blockMode[i - 1] = QrMode.BINARY;
}
break;
case NUMERIC:
if (blockLength[i - 1] < tribus(version, 3, 4, 5)) {
blockMode[i - 1] = QrMode.BINARY;
}
break;
}
}
 
if (blockMode[i] == QrMode.ALPHANUM && blockMode[i - 1] == QrMode.NUMERIC) {
if (blockLength[i - 1] < tribus(version, 6, 8, 10)) {
blockMode[i - 1] = QrMode.ALPHANUM;
}
}
}
}
 
// ZINT NOTE: this method is different from the original Zint code in that it creates a
// new array to hold the optimized values and returns it, rather than modifying the
// original array; this allows this method to be called as many times as we want without
// worrying about side effects
 
final QrMode[] optimized = new QrMode[inputMode.length];
 
j = 0;
for (int block = 0; block < blockCount; block++) {
currentMode = blockMode[block];
for (i = 0; i < blockLength[block]; i++) {
optimized[j] = currentMode;
j++;
}
}
 
return optimized;
}
 
/** Find the length of the block starting from 'start'. */
private static int blockLength(final int start, final QrMode[] inputMode) {
 
final QrMode mode = inputMode[start];
int count = 0;
final int i = start;
 
do {
count++;
} while (i + count < inputMode.length && inputMode[i + count] == mode);
 
return count;
}
 
/** Choose from three numbers based on version. */
private static int tribus(final int version, final int a, final int b, final int c) {
if (version < 10) {
return a;
} else if (version >= 10 && version <= 26) {
return b;
} else {
return c;
}
}
 
/** Returns true if input is in the Alphanumeric set (see Table J.1) */
private static boolean isAlpha(final int c) {
return c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == ' ' || c == '$' || c == '%' || c == '*' || c == '+' || c == '-' || c == '.' || c == '/' || c == ':';
}
 
/** Returns true if input is in the Numeric set (see Table J.1) */
private static boolean isNumeric(final int c) {
return c >= '0' && c <= '9';
}
 
/** Converts input data to a binary stream and adds padding. */
private void qrBinary(final int[] datastream, final int version, final int target_binlen, final QrMode[] inputMode, final int[] inputData, final boolean gs1, final int eciMode,
final int est_binlen) {
 
// TODO: make encodeInfo a StringBuilder, make this method static?
 
int position = 0;
int short_data_block_length, i;
int padbits;
int current_binlen, current_bytes;
int toggle;
QrMode data_block;
 
final StringBuilder binary = new StringBuilder(est_binlen + 12);
 
if (gs1) {
binary.append("0101"); /* FNC1 */
}
 
if (eciMode != 3) {
binary.append("0111"); /* ECI (Table 4) */
if (eciMode <= 127) {
binaryAppend(eciMode, 8, binary); /* 000000 to 000127 */
} else if (eciMode <= 16383) {
binaryAppend(0x8000 + eciMode, 16, binary); /* 000000 to 016383 */
} else {
binaryAppend(0xC00000 + eciMode, 24, binary); /* 000000 to 999999 */
}
}
 
info("Encoding: ");
 
do {
data_block = inputMode[position];
short_data_block_length = 0;
do {
short_data_block_length++;
} while (short_data_block_length + position < inputMode.length && inputMode[position + short_data_block_length] == data_block);
 
switch (data_block) {
 
case KANJI:
/* Kanji mode */
/* Mode indicator */
binary.append("1000");
 
/* Character count indicator */
binaryAppend(short_data_block_length, tribus(version, 8, 10, 12), binary);
 
info("KNJI ");
 
/* Character representation */
for (i = 0; i < short_data_block_length; i++) {
int jis = inputData[position + i];
if (jis >= 0x8140 && jis <= 0x9ffc) {
jis -= 0x8140;
} else if (jis >= 0xe040 && jis <= 0xebbf) {
jis -= 0xc140;
}
final int prod = (jis >> 8) * 0xc0 + (jis & 0xff);
binaryAppend(prod, 13, binary);
infoSpace(prod);
}
 
break;
 
case BINARY:
/* Byte mode */
/* Mode indicator */
binary.append("0100");
 
/* Character count indicator */
binaryAppend(short_data_block_length, tribus(version, 8, 16, 16), binary);
 
info("BYTE ");
 
/* Character representation */
for (i = 0; i < short_data_block_length; i++) {
int b = inputData[position + i];
if (b == FNC1) {
b = 0x1d; /* FNC1 */
}
binaryAppend(b, 8, binary);
infoSpace(b);
}
 
break;
 
case ALPHANUM:
/* Alphanumeric mode */
/* Mode indicator */
binary.append("0010");
 
/* If in GS1 mode, expand FNC1 -> '%' and expand '%' -> '%%' in a new array */
int percentCount = 0;
if (gs1) {
for (i = 0; i < short_data_block_length; i++) {
if (inputData[position + i] == '%') {
percentCount++;
}
}
}
final int[] inputExpanded = new int[short_data_block_length + percentCount];
percentCount = 0;
for (i = 0; i < short_data_block_length; i++) {
final int c = inputData[position + i];
if (c == FNC1) {
inputExpanded[i + percentCount] = '%'; /* FNC1 */
} else {
inputExpanded[i + percentCount] = c;
if (gs1 && c == '%') {
percentCount++;
inputExpanded[i + percentCount] = c;
}
}
}
 
/* Character count indicator */
binaryAppend(inputExpanded.length, tribus(version, 9, 11, 13), binary);
 
info("ALPH ");
 
/* Character representation */
for (i = 0; i + 1 < inputExpanded.length; i += 2) {
final int first = positionOf((char) inputExpanded[i], RHODIUM);
final int second = positionOf((char) inputExpanded[i + 1], RHODIUM);
final int prod = first * 45 + second;
final int count = 2;
binaryAppend(prod, 1 + 5 * count, binary);
infoSpace(prod);
}
if (inputExpanded.length % 2 != 0) {
final int first = positionOf((char) inputExpanded[inputExpanded.length - 1], RHODIUM);
final int prod = first;
final int count = 1;
binaryAppend(prod, 1 + 5 * count, binary);
infoSpace(prod);
}
 
break;
 
case NUMERIC:
/* Numeric mode */
/* Mode indicator */
binary.append("0001");
 
/* Character count indicator */
binaryAppend(short_data_block_length, tribus(version, 10, 12, 14), binary);
 
info("NUMB ");
 
/* Character representation */
i = 0;
while (i < short_data_block_length) {
 
final int first = Character.getNumericValue(inputData[position + i]);
int count = 1;
int prod = first;
 
if (i + 1 < short_data_block_length) {
final int second = Character.getNumericValue(inputData[position + i + 1]);
count = 2;
prod = prod * 10 + second;
 
if (i + 2 < short_data_block_length) {
final int third = Character.getNumericValue(inputData[position + i + 2]);
count = 3;
prod = prod * 10 + third;
}
}
 
binaryAppend(prod, 1 + 3 * count, binary);
 
infoSpace(prod);
 
i += count;
}
 
break;
}
 
position += short_data_block_length;
 
} while (position < inputMode.length);
 
infoLine();
 
/* Terminator */
binary.append("0000");
 
current_binlen = binary.length();
padbits = 8 - current_binlen % 8;
if (padbits == 8) {
padbits = 0;
}
current_bytes = (current_binlen + padbits) / 8;
 
/* Padding bits */
for (i = 0; i < padbits; i++) {
binary.append('0');
}
 
/* Put data into 8-bit codewords */
for (i = 0; i < current_bytes; i++) {
datastream[i] = 0x00;
for (int p = 0; p < 8; p++) {
if (binary.charAt(i * 8 + p) == '1') {
datastream[i] += 0x80 >> p;
}
}
}
 
/* Add pad codewords */
toggle = 0;
for (i = current_bytes; i < target_binlen; i++) {
if (toggle == 0) {
datastream[i] = 0xec;
toggle = 1;
} else {
datastream[i] = 0x11;
toggle = 0;
}
}
 
info("Codewords: ");
for (i = 0; i < target_binlen; i++) {
infoSpace(datastream[i]);
}
infoLine();
}
 
private static void binaryAppend(final int value, final int length, final StringBuilder binary) {
final int start = 0x01 << length - 1;
for (int i = 0; i < length; i++) {
if ((value & start >> i) != 0) {
binary.append('1');
} else {
binary.append('0');
}
}
}
 
/**
* Splits data into blocks, adds error correction and then interleaves the blocks and error
* correction data.
*/
private static void addEcc(final int[] fullstream, final int[] datastream, final int version, final int data_cw, final int blocks) {
 
final int ecc_cw = QR_TOTAL_CODEWORDS[version - 1] - data_cw;
final int short_data_block_length = data_cw / blocks;
final int qty_long_blocks = data_cw % blocks;
final int qty_short_blocks = blocks - qty_long_blocks;
final int ecc_block_length = ecc_cw / blocks;
int i, j, length_this_block, posn;
 
final int[] data_block = new int[short_data_block_length + 2];
final int[] ecc_block = new int[ecc_block_length + 2];
final int[] interleaved_data = new int[data_cw + 2];
final int[] interleaved_ecc = new int[ecc_cw + 2];
 
posn = 0;
 
for (i = 0; i < blocks; i++) {
if (i < qty_short_blocks) {
length_this_block = short_data_block_length;
} else {
length_this_block = short_data_block_length + 1;
}
 
for (j = 0; j < ecc_block_length; j++) {
ecc_block[j] = 0;
}
 
for (j = 0; j < length_this_block; j++) {
data_block[j] = datastream[posn + j];
}
 
final ReedSolomon rs = new ReedSolomon();
rs.init_gf(0x11d);
rs.init_code(ecc_block_length, 0);
rs.encode(length_this_block, data_block);
 
for (j = 0; j < ecc_block_length; j++) {
ecc_block[j] = rs.getResult(j);
}
 
for (j = 0; j < short_data_block_length; j++) {
interleaved_data[j * blocks + i] = data_block[j];
}
 
if (i >= qty_short_blocks) {
interleaved_data[short_data_block_length * blocks + i - qty_short_blocks] = data_block[short_data_block_length];
}
 
for (j = 0; j < ecc_block_length; j++) {
interleaved_ecc[j * blocks + i] = ecc_block[ecc_block_length - j - 1];
}
 
posn += length_this_block;
}
 
for (j = 0; j < data_cw; j++) {
fullstream[j] = interleaved_data[j];
}
for (j = 0; j < ecc_cw; j++) {
fullstream[j + data_cw] = interleaved_ecc[j];
}
}
 
private static void setupGrid(final int[] grid, final int size, final int version) {
 
int i;
boolean toggle = true;
 
/* Add timing patterns */
for (i = 0; i < size; i++) {
if (toggle) {
grid[6 * size + i] = 0x21;
grid[i * size + 6] = 0x21;
toggle = false;
} else {
grid[6 * size + i] = 0x20;
grid[i * size + 6] = 0x20;
toggle = true;
}
}
 
/* Add finder patterns */
placeFinder(grid, size, 0, 0);
placeFinder(grid, size, 0, size - 7);
placeFinder(grid, size, size - 7, 0);
 
/* Add separators */
for (i = 0; i < 7; i++) {
grid[7 * size + i] = 0x10;
grid[i * size + 7] = 0x10;
grid[7 * size + size - 1 - i] = 0x10;
grid[i * size + size - 8] = 0x10;
grid[(size - 8) * size + i] = 0x10;
grid[(size - 1 - i) * size + 7] = 0x10;
}
grid[7 * size + 7] = 0x10;
grid[7 * size + size - 8] = 0x10;
grid[(size - 8) * size + 7] = 0x10;
 
/* Add alignment patterns */
if (version != 1) {
/* Version 1 does not have alignment patterns */
final int loopsize = QR_ALIGN_LOOPSIZE[version - 1];
for (int x = 0; x < loopsize; x++) {
for (int y = 0; y < loopsize; y++) {
final int xcoord = QR_TABLE_E1[(version - 2) * 7 + x];
final int ycoord = QR_TABLE_E1[(version - 2) * 7 + y];
if ((grid[ycoord * size + xcoord] & 0x10) == 0) {
placeAlign(grid, size, xcoord, ycoord);
}
}
}
}
 
/* Reserve space for format information */
for (i = 0; i < 8; i++) {
grid[8 * size + i] += 0x20;
grid[i * size + 8] += 0x20;
grid[8 * size + size - 1 - i] = 0x20;
grid[(size - 1 - i) * size + 8] = 0x20;
}
grid[8 * size + 8] += 0x20;
grid[(size - 1 - 7) * size + 8] = 0x21; /* Dark Module from Figure 25 */
 
/* Reserve space for version information */
if (version >= 7) {
for (i = 0; i < 6; i++) {
grid[(size - 9) * size + i] = 0x20;
grid[(size - 10) * size + i] = 0x20;
grid[(size - 11) * size + i] = 0x20;
grid[i * size + size - 9] = 0x20;
grid[i * size + size - 10] = 0x20;
grid[i * size + size - 11] = 0x20;
}
}
}
 
private static void placeFinder(final int[] grid, final int size, final int x, final int y) {
 
final int[] finder = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 };
 
for (int xp = 0; xp < 7; xp++) {
for (int yp = 0; yp < 7; yp++) {
if (finder[xp + 7 * yp] == 1) {
grid[(yp + y) * size + xp + x] = 0x11;
} else {
grid[(yp + y) * size + xp + x] = 0x10;
}
}
}
}
 
private static void placeAlign(final int[] grid, final int size, int x, int y) {
 
final int[] alignment = { 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 };
 
x -= 2;
y -= 2; /* Input values represent centre of pattern */
 
for (int xp = 0; xp < 5; xp++) {
for (int yp = 0; yp < 5; yp++) {
if (alignment[xp + 5 * yp] == 1) {
grid[(yp + y) * size + xp + x] = 0x11;
} else {
grid[(yp + y) * size + xp + x] = 0x10;
}
}
}
}
 
private static void populateGrid(final int[] grid, final int size, final int[] fullstream, final int cw) {
 
boolean goingUp = true;
int row = 0; /* right hand side */
 
int i, n, y;
 
n = cw * 8;
y = size - 1;
i = 0;
do {
int x = size - 2 - row * 2;
if (x < 6) {
x--; /* skip over vertical timing pattern */
}
 
if ((grid[y * size + x + 1] & 0xf0) == 0) {
if (cwbit(fullstream, i)) {
grid[y * size + x + 1] = 0x01;
} else {
grid[y * size + x + 1] = 0x00;
}
i++;
}
 
if (i < n) {
if ((grid[y * size + x] & 0xf0) == 0) {
if (cwbit(fullstream, i)) {
grid[y * size + x] = 0x01;
} else {
grid[y * size + x] = 0x00;
}
i++;
}
}
 
if (goingUp) {
y--;
} else {
y++;
}
if (y == -1) {
/* reached the top */
row++;
y = 0;
goingUp = false;
}
if (y == size) {
/* reached the bottom */
row++;
y = size - 1;
goingUp = true;
}
} while (i < n);
}
 
private static boolean cwbit(final int[] fullstream, final int i) {
return (fullstream[i / 8] & 0x80 >> i % 8) != 0;
}
 
private static int applyBitmask(final int[] grid, final int size, final EccLevel ecc_level) {
 
int x, y;
char p;
int pattern;
int best_val, best_pattern;
final int[] penalty = new int[8];
final byte[] mask = new byte[size * size];
final byte[] eval = new byte[size * size];
 
/* Perform data masking */
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
mask[y * size + x] = 0x00;
// all eight bit mask variants are encoded in the 8 bits of the bytes that make up
// the mask array
if ((grid[y * size + x] & 0xf0) == 0) { // exclude areas not to be masked
if ((y + x & 1) == 0) {
mask[y * size + x] += (byte) 0x01;
}
if ((y & 1) == 0) {
mask[y * size + x] += (byte) 0x02;
}
if (x % 3 == 0) {
mask[y * size + x] += (byte) 0x04;
}
if ((y + x) % 3 == 0) {
mask[y * size + x] += (byte) 0x08;
}
if ((y / 2 + x / 3 & 1) == 0) {
mask[y * size + x] += (byte) 0x10;
}
if ((y * x & 1) + y * x % 3 == 0) {
mask[y * size + x] += (byte) 0x20;
}
if (((y * x & 1) + y * x % 3 & 1) == 0) {
mask[y * size + x] += (byte) 0x40;
}
if (((y + x & 1) + y * x % 3 & 1) == 0) {
mask[y * size + x] += (byte) 0x80;
}
}
}
}
 
/* Apply data masks to grid, result in eval */
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
if ((grid[y * size + x] & 0x01) != 0) {
p = 0xff;
} else {
p = 0x00;
}
eval[y * size + x] = (byte) (mask[y * size + x] ^ p);
}
}
 
/* Evaluate result */
for (pattern = 0; pattern < 8; pattern++) {
addFormatInfoEval(eval, size, ecc_level, pattern);
penalty[pattern] = evaluate(eval, size, pattern);
}
 
best_pattern = 0;
best_val = penalty[0];
for (pattern = 1; pattern < 8; pattern++) {
if (penalty[pattern] < best_val) {
best_pattern = pattern;
best_val = penalty[pattern];
}
}
 
/* Apply mask */
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
if ((mask[y * size + x] & 0x01 << best_pattern) != 0) {
if ((grid[y * size + x] & 0x01) != 0) {
grid[y * size + x] = 0x00;
} else {
grid[y * size + x] = 0x01;
}
}
}
}
 
return best_pattern;
}
 
/** Adds format information to eval. */
private static void addFormatInfoEval(final byte[] eval, final int size, final EccLevel ecc_level, final int pattern) {
 
int format = pattern;
int seq;
int i;
 
switch (ecc_level) {
case L:
format += 0x08;
break;
case Q:
format += 0x18;
break;
case H:
format += 0x10;
break;
}
 
seq = QR_ANNEX_C[format];
 
for (i = 0; i < 6; i++) {
eval[i * size + 8] = (byte) ((seq >> i & 0x01) != 0 ? 0x01 >> pattern : 0x00);
}
 
for (i = 0; i < 8; i++) {
eval[8 * size + size - i - 1] = (byte) ((seq >> i & 0x01) != 0 ? 0x01 >> pattern : 0x00);
}
 
for (i = 0; i < 6; i++) {
eval[8 * size + 5 - i] = (byte) ((seq >> i + 9 & 0x01) != 0 ? 0x01 >> pattern : 0x00);
}
 
for (i = 0; i < 7; i++) {
eval[(size - 7 + i) * size + 8] = (byte) ((seq >> i + 8 & 0x01) != 0 ? 0x01 >> pattern : 0x00);
}
 
eval[7 * size + 8] = (byte) ((seq >> 6 & 0x01) != 0 ? 0x01 >> pattern : 0x00);
eval[8 * size + 8] = (byte) ((seq >> 7 & 0x01) != 0 ? 0x01 >> pattern : 0x00);
eval[8 * size + 7] = (byte) ((seq >> 8 & 0x01) != 0 ? 0x01 >> pattern : 0x00);
}
 
private static int evaluate(final byte[] eval, final int size, final int pattern) {
 
int x, y, block, weight;
int result = 0;
int state;
int p;
int dark_mods;
int percentage, k;
int a, b, afterCount, beforeCount;
final byte[] local = new byte[size * size];
 
// all eight bit mask variants have been encoded in the 8 bits of the bytes
// that make up the grid array; select them for evaluation according to the
// desired pattern
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
if ((eval[y * size + x] & 0x01 << pattern) != 0) {
local[y * size + x] = '1';
} else {
local[y * size + x] = '0';
}
}
}
 
/* Test 1: Adjacent modules in row/column in same colour */
/* Vertical */
for (x = 0; x < size; x++) {
state = local[x];
block = 0;
for (y = 0; y < size; y++) {
if (local[y * size + x] == state) {
block++;
} else {
if (block > 5) {
result += 3 + block - 5;
}
block = 0;
state = local[y * size + x];
}
}
if (block > 5) {
result += 3 + block - 5;
}
}
 
/* Horizontal */
for (y = 0; y < size; y++) {
state = local[y * size];
block = 0;
for (x = 0; x < size; x++) {
if (local[y * size + x] == state) {
block++;
} else {
if (block > 5) {
result += 3 + block - 5;
}
block = 0;
state = local[y * size + x];
}
}
if (block > 5) {
result += 3 + block - 5;
}
}
 
/* Test 2: Block of modules in same color */
for (x = 0; x < size - 1; x++) {
for (y = 0; y < size - 1; y++) {
if (local[y * size + x] == local[(y + 1) * size + x] && local[y * size + x] == local[y * size + x + 1] && local[y * size + x] == local[(y + 1) * size + x + 1]) {
result += 3;
}
}
}
 
/* Test 3: 1:1:3:1:1 ratio pattern in row/column */
/* Vertical */
for (x = 0; x < size; x++) {
for (y = 0; y < size - 7; y++) {
p = 0;
for (weight = 0; weight < 7; weight++) {
if (local[(y + weight) * size + x] == '1') {
p += 0x40 >> weight;
}
}
if (p == 0x5d) {
/* Pattern found, check before and after */
beforeCount = 0;
for (b = y - 4; b < y; b++) {
if (b < 0) {
beforeCount++;
} else {
if (local[b * size + x] == '0') {
beforeCount++;
} else {
beforeCount = 0;
}
}
}
 
afterCount = 0;
for (a = y + 7; a <= y + 10; a++) {
if (a >= size) {
afterCount++;
} else {
if (local[a * size + x] == '0') {
afterCount++;
} else {
afterCount = 0;
}
}
}
 
if (beforeCount == 4 || afterCount == 4) {
// Pattern is preceded or followed by light area 4 modules wide
result += 40;
}
}
}
}
 
/* Horizontal */
for (y = 0; y < size; y++) {
for (x = 0; x < size - 7; x++) {
p = 0;
for (weight = 0; weight < 7; weight++) {
if (local[y * size + x + weight] == '1') {
p += 0x40 >> weight;
}
}
if (p == 0x5d) {
/* Pattern found, check before and after */
beforeCount = 0;
for (b = x - 4; b < x; b++) {
if (b < 0) {
beforeCount++;
} else {
if (local[y * size + b] == '0') {
beforeCount++;
} else {
beforeCount = 0;
}
}
}
 
afterCount = 0;
for (a = x + 7; a <= x + 10; a++) {
if (a >= size) {
afterCount++;
} else {
if (local[y * size + a] == '0') {
afterCount++;
} else {
afterCount = 0;
}
}
}
 
if (beforeCount == 4 || afterCount == 4) {
// Pattern is preceded or followed by light area 4 modules wide
result += 40;
}
}
}
}
 
/* Test 4: Proportion of dark modules in entire symbol */
dark_mods = 0;
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
if (local[y * size + x] == '1') {
dark_mods++;
}
}
}
percentage = 100 * (dark_mods / (size * size));
if (percentage <= 50) {
k = (100 - percentage - 50) / 5;
} else {
k = (percentage - 50) / 5;
}
 
result += 10 * k;
 
return result;
}
 
/* Adds format information to grid. */
private static void addFormatInfo(final int[] grid, final int size, final EccLevel ecc_level, final int pattern) {
 
int format = pattern;
int seq;
int i;
 
switch (ecc_level) {
case L:
format += 0x08;
break;
case Q:
format += 0x18;
break;
case H:
format += 0x10;
break;
}
 
seq = QR_ANNEX_C[format];
 
for (i = 0; i < 6; i++) {
grid[i * size + 8] += seq >> i & 0x01;
}
 
for (i = 0; i < 8; i++) {
grid[8 * size + size - i - 1] += seq >> i & 0x01;
}
 
for (i = 0; i < 6; i++) {
grid[8 * size + 5 - i] += seq >> i + 9 & 0x01;
}
 
for (i = 0; i < 7; i++) {
grid[(size - 7 + i) * size + 8] += seq >> i + 8 & 0x01;
}
 
grid[7 * size + 8] += seq >> 6 & 0x01;
grid[8 * size + 8] += seq >> 7 & 0x01;
grid[8 * size + 7] += seq >> 8 & 0x01;
}
 
/** Adds version information. */
private static void addVersionInfo(final int[] grid, final int size, final int version) {
// TODO: Zint masks with 0x41 instead of 0x01; which is correct?
// https://sourceforge.net/p/zint/tickets/110/
final int version_data = QR_ANNEX_D[version - 7];
for (int i = 0; i < 6; i++) {
grid[(size - 11) * size + i] += version_data >> i * 3 & 0x01;
grid[(size - 10) * size + i] += version_data >> i * 3 + 1 & 0x01;
grid[(size - 9) * size + i] += version_data >> i * 3 + 2 & 0x01;
grid[i * size + size - 11] += version_data >> i * 3 & 0x01;
grid[i * size + size - 10] += version_data >> i * 3 + 1 & 0x01;
grid[i * size + size - 9] += version_data >> i * 3 + 2 & 0x01;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/RoyalMail4State.java
New file
0,0 → 1,122
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
import java.awt.geom.Rectangle2D;
import java.util.Locale;
 
/**
* <p>
* Encodes data according to the Royal Mail 4-State Country Code.
*
* <p>
* Data input can consist of numbers 0-9 and letters A-Z and usually includes delivery postcode
* followed by house number. A check digit is calculated and added.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class RoyalMail4State extends Symbol {
 
private static final String[] ROYAL_TABLE = { "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT",
"ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", "FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT" };
 
private static final char[] KR_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z' };
 
@Override
protected void encode() {
String dest;
int i, top = 0, bottom = 0;
int row, column;
int index;
 
this.content = this.content.toUpperCase(Locale.ENGLISH);
if (!this.content.matches("[0-9A-Z]+")) {
throw new OkapiException("Invalid characters in data");
}
dest = "A";
 
for (i = 0; i < this.content.length(); i++) {
index = positionOf(this.content.charAt(i), KR_SET);
dest += ROYAL_TABLE[index];
top += (index + 1) % 6;
bottom += (index / 6 + 1) % 6;
}
 
/* calculate check digit */
row = top % 6 - 1;
column = bottom % 6 - 1;
if (row == -1) {
row = 5;
}
if (column == -1) {
column = 5;
}
final int check = 6 * row + column;
dest += ROYAL_TABLE[check];
infoLine("Check Digit: " + check);
 
/* Stop character */
dest += "F";
 
infoLine("Encoding: " + dest);
this.readable = "";
this.pattern = new String[1];
this.pattern[0] = dest;
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
}
 
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
 
this.rectangles.clear();
x = 0;
w = 1;
y = 0;
h = 0;
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
switch (this.pattern[0].charAt(xBlock)) {
case 'A':
y = 0;
h = 5;
break;
case 'D':
y = 3;
h = 5;
break;
case 'F':
y = 0;
h = 8;
break;
case 'T':
y = 3;
h = 2;
break;
}
 
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
this.rectangles.add(rect);
 
x += 2;
}
this.symbol_width = this.pattern[0].length() * 3;
this.symbol_height = 8;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code93.java
New file
0,0 → 1,189
/*
* Copyright 2014-2015 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
/**
* <p>
* Implements <a href="http://en.wikipedia.org/wiki/Code_93">Code 93</a>.
*
* <p>
* Supports encoding of 7-bit ASCII text. Two check digits are added.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class Code93 extends Symbol {
 
/**
* Code 93 control characters, indexed by ASCII codes (NOTE: a = Ctrl $, b = Ctrl %, c = Ctrl /,
* d = Ctrl + for sequences of two characters).
*/
private static final String[] CODE_93_CTRL = { "bU", "aA", "aB", "aC", "aD", "aE", "aF", "aG", "aH", "aI", "aJ", "aK", "aL", "aM", "aN", "aO", "aP", "aQ", "aR", "aS", "aT", "aU", "aV", "aW", "aX",
"aY", "aZ", "bA", "bB", "bC", "bD", "bE", " ", "cA", "cB", "cC", "$", "%", "cF", "cG", "cH", "cI", "cJ", "+", "cL", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "cZ",
"bF", "bG", "bH", "bI", "bJ", "bV", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bK", "bL", "bM",
"bN", "bO", "bW", "dA", "dB", "dC", "dD", "dE", "dF", "dG", "dH", "dI", "dJ", "dK", "dL", "dM", "dN", "dO", "dP", "dQ", "dR", "dS", "dT", "dU", "dV", "dW", "dX", "dY", "dZ", "bP", "bQ",
"bR", "bS", "bT" };
 
/**
* Mapping of control characters to pattern table index (NOTE: a = Ctrl $, b = Ctrl %, c = Ctrl
* /, d = Ctrl + for sequences of two characters).
*/
private static final char[] CODE_93_LOOKUP = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%', 'a', 'b', 'c', 'd' };
 
/** Code 93 pattern table. */
private static final String[] CODE_93_TABLE = { "131112", "111213", "111312", "111411", "121113", "121212", "121311", "111114", "131211", "141111", "211113", "211212", "211311", "221112",
"221211", "231111", "112113", "112212", "112311", "122112", "132111", "111123", "111222", "111321", "121122", "131121", "212112", "212211", "211122", "211221", "221121", "222111",
"112122", "112221", "122121", "123111", "121131", "311112", "311211", "321111", "112131", "113121", "211131", "121221", "312111", "311121", "122211" };
 
/** Whether or not to show check digits in the human-readable text. */
private boolean showCheckDigits = true;
 
/** Optional start/stop delimiter to be shown in the human-readable text. */
private Character startStopDelimiter;
 
/**
* Sets whether or not to show check digits in the human-readable text (defaults to
* <code>true</code>).
*
* @param showCheckDigits whether or not to show check digits in the human-readable text
*/
public void setShowCheckDigits(final boolean showCheckDigits) {
this.showCheckDigits = showCheckDigits;
}
 
/**
* Returns whether or not this symbol shows check digits in the human-readable text.
*
* @return whether or not this symbol shows check digits in the human-readable text
*/
public boolean getShowCheckDigits() {
return this.showCheckDigits;
}
 
/**
* Sets an optional start/stop delimiter to be shown in the human-readable text (defaults to
* <code>null</code>).
*
* @param startStopDelimiter an optional start/stop delimiter to be shown in the human-readable
* text
*/
public void setStartStopDelimiter(final Character startStopDelimiter) {
this.startStopDelimiter = startStopDelimiter;
}
 
/**
* Returns the optional start/stop delimiter to be shown in the human-readable text.
*
* @return the optional start/stop delimiter to be shown in the human-readable text
*/
public Character getStartStopDelimiter() {
return this.startStopDelimiter;
}
 
/** {@inheritDoc} */
@Override
protected void encode() {
 
final char[] controlChars = toControlChars(this.content);
int l = controlChars.length;
 
if (!this.content.matches("[\u0000-\u007F]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
final int[] values = new int[controlChars.length + 2];
for (int i = 0; i < l; i++) {
values[i] = positionOf(controlChars[i], CODE_93_LOOKUP);
}
 
final int c = calculateCheckDigitC(values, l);
values[l] = c;
l++;
 
final int k = calculateCheckDigitK(values, l);
values[l] = k;
l++;
 
this.readable = this.content;
if (this.showCheckDigits) {
this.readable = this.readable + CODE_93_LOOKUP[c] + CODE_93_LOOKUP[k];
}
if (this.startStopDelimiter != null) {
this.readable = this.startStopDelimiter + this.readable + this.startStopDelimiter;
}
 
infoLine("Check Digit C: " + c);
infoLine("Check Digit K: " + k);
this.pattern = new String[] { toPattern(values) };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private static char[] toControlChars(final String s) {
final StringBuilder buffer = new StringBuilder();
final char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
final int asciiCode = chars[i];
buffer.append(CODE_93_CTRL[asciiCode]);
}
return buffer.toString().toCharArray();
}
 
private static int calculateCheckDigitC(final int[] values, final int length) {
int c = 0;
int weight = 1;
for (int i = length - 1; i >= 0; i--) {
c += values[i] * weight;
weight++;
if (weight == 21) {
weight = 1;
}
}
c = c % 47;
return c;
}
 
private static int calculateCheckDigitK(final int[] values, final int length) {
int k = 0;
int weight = 1;
for (int i = length - 1; i >= 0; i--) {
k += values[i] * weight;
weight++;
if (weight == 16) {
weight = 1;
}
}
k = k % 47;
return k;
}
 
private static String toPattern(final int[] values) {
final StringBuilder buffer = new StringBuilder("111141");
for (int i = 0; i < values.length; i++) {
buffer.append(CODE_93_TABLE[values[i]]);
}
buffer.append("1111411");
return buffer.toString();
}
 
/** {@inheritDoc} */
@Override
protected int[] getCodewords() {
return getPatternAsCodewords(6);
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/HumanReadableAlignment.java
New file
0,0 → 1,34
/*
* Copyright 2018 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
/**
* The text alignment of a bar code's human-readable text.
*/
public enum HumanReadableAlignment {
 
/** Left-align the human-readable text. */
LEFT,
 
/** Right-align the human-readable text. */
RIGHT,
 
/** Center the human-readable text. */
CENTER,
 
/** Justify the human-readable text by adjusting the spaces between the characters. */
JUSTIFY
 
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code49.java
New file
0,0 → 1,850
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
import java.awt.geom.Rectangle2D;
import java.nio.charset.StandardCharsets;
 
/**
* <p>
* Implements Code 49 according to ANSI/AIM-BC6-2000.
*
* <p>
* Supports full 7-bit ASCII input up to a maximum of 49 characters or 81 numeric digits. GS1 data
* encoding is also supported.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Code49 extends Symbol {
 
private static final String[] C49_TABLE7 = {
/* Table 7: Code 49 ASCII Chart */
"! ", "!A", "!B", "!C", "!D", "!E", "!F", "!G", "!H", "!I", "!J", "!K", "!L", "!M", "!N", "!O", "!P", "!Q", "!R", "!S", "!T", "!U", "!V", "!W", "!X", "!Y", "!Z", "!1", "!2", "!3", "!4",
"!5", " ", "!6", "!7", "!8", "$", "%", "!9", "!0", "!-", "!.", "!$", "+", "!/", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!+", "&1", "&2", "&3", "&4", "&5", "&6",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "&7", "&8", "&9", "&0", "&-", "&.", "&A", "&B", "&C",
"&D", "&E", "&F", "&G", "&H", "&I", "&J", "&K", "&L", "&M", "&N", "&O", "&P", "&Q", "&R", "&S", "&T", "&U", "&V", "&W", "&X", "&Y", "&Z", "&$", "&/", "&+", "&%", "& " };
 
/* Table 5: Check Character Weighting Values */
private static final int[] C49_X_WEIGHT = { 1, 9, 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10 };
 
private static final int[] C49_Y_WEIGHT = { 9, 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10, 24 };
 
private static final int[] C49_Z_WEIGHT = { 31, 26, 2, 12, 17, 23, 37, 18, 22, 6, 27, 44, 15, 43, 39, 11, 13, 5, 41, 33, 36, 8, 4, 32, 3, 19, 40, 25, 29, 10, 24, 30 };
 
private static final String[] C49_TABLE4 = {
/* Table 4: Row Parity Pattern for Code 49 Symbols */
"OEEO", "EOEO", "OOEE", "EEOO", "OEOE", "EOOE", "OOOO", "EEEE" };
 
private static final String[] C49_APPXE_EVEN = {
/* Appendix E - Code 49 Encodation Patterns (Even Symbol Character Parity) */
/* Column 1 */
"11521132", "25112131", "14212132", "25121221", "14221222", "12412132", "23321221", "12421222", "21521221", "15112222", "15121312", "13312222", "24221311", "13321312", "11512222",
"22421311", "11521312", "25112311", "14212312", "23312311", "12412312", "21512311", "16121131", "14321131", "12521131", "15212131", "15221221", "13412131", "13421221", "11612131",
"16112221", "16121311", "14312221", "14321311", "12512221", "12521311", "15212311", "13412311", "11612311", "11131135", "31131133", "51131131", "21122134", "41122132", "21131224",
"41131222", "11113135", "31113133", "51113131", "11122225", "31122223", "51122221", "11131315", "31131313", "51131311", "21113224", "41113222", "21122314",
/* Column 2 */
"41122312", "11113315", "31113313", "51113311", "12131134", "32131132", "21231133", "41231131", "22122133", "42122131", "11222134", "22131223", "42131221", "11231224", "31231222",
"12113134", "32113132", "12122224", "32122222", "12131314", "32131312", "21231313", "41231311", "22113223", "42113221", "11213224", "22122313", "42122311", "11222314", "31222312",
"12113314", "32113312", "21213313", "41213311", "13131133", "33131131", "22231132", "11331133", "31331131", "23122132", "12222133", "23131222", "12231223", "32231221", "21331222",
"13113133", "33113131", "13122223", "33122221", "11313133", "13131313", "33131311", "11322223", "22231312", "11331313", "31331311", "23113222", "12213223",
/* Column 3 */
"23122312", "12222313", "32222311", "21322312", "13113313", "33113311", "22213312", "11313313", "31313311", "14131132", "23231131", "12331132", "21431131", "24122131", "13222132",
"24131221", "13231222", "11422132", "22331221", "11431222", "14113132", "14122222", "12313132", "14131312", "12322222", "23231311", "12331312", "21431311", "24113221", "13213222",
"24122311", "13222312", "11413222", "22322311", "11422312", "14113312", "23213311", "12313312", "21413311", "15131131", "13331131", "14222131", "14231221", "12422131", "12431221",
"15113131", "15122221", "13313131", "15131311", "13322221", "11513131", "13331311", "11522221", "14213221", "14222311", "12413221", "12422311", "15113311",
/* Column 4 */
"13313311", "11513311", "11141134", "31141132", "21132133", "41132131", "21141223", "41141221", "11123134", "31123132", "11132224", "31132222", "11141314", "31141312", "21114133",
"41114131", "21123223", "41123221", "21132313", "41132311", "11114224", "31114222", "11123314", "31123312", "21114313", "41114311", "12141133", "32141131", "21241132", "22132132",
"11232133", "22141222", "11241223", "31241221", "12123133", "32123131", "12132223", "32132221", "12141313", "32141311", "21241312", "22114132", "11214133", "22123222", "11223223",
"22132312", "11232313", "31232311", "12114223", "32114221", "12123313", "32123311", "21223312", "22114312", "11214313", "31214311", "13141132", "22241131",
/* Column 5 */
"11341132", "23132131", "12232132", "23141221", "12241222", "21341221", "13123132", "13132222", "11323132", "13141312", "11332222", "22241311", "11341312", "23114131", "12214132",
"23123221", "12223222", "23132311", "12232312", "21332311", "13114222", "13123312", "11314222", "22223311", "11323312", "23114311", "12214312", "21314311", "14141131", "12341131",
"13232131", "13241221", "11432131", "14123131", "14132221", "12323131", "14141311", "12332221", "12341311", "13214131", "13223221", "11414131", "13232311", "11423221", "11432311",
"14114221", "14123311", "12314221", "12323311", "13214311", "11414311", "11151133", "31151131", "21142132", "21151222", "11133133", "31133131", "11142223",
/* Column 6 */
"31142221", "11151313", "31151311", "21124132", "21133222", "21142312", "11115133", "31115131", "11124223", "31124221", "11133313", "31133311", "21115222", "21124312", "12151132",
"21251131", "22142131", "11242132", "22151221", "11251222", "12133132", "12142222", "12151312", "21251311", "22124131", "11224132", "22133221", "11233222", "22142311", "11242312",
"12115132", "12124222", "12133312", "21233311", "22115221", "11215222", "22124311", "11224312", "13151131", "12242131", "12251221", "13133131", "13142221", "11333131", "13151311",
"11342221", "12224131", "12233221", "12242311", "13115131", "13124221", "11315131", "13133311", "11324221", "11333311", "12215221", "12224311", "11161132",
/* Column 7 */
"21152131", "21161221", "11143132", "11152222", "11161312", "21134131", "21143221", "21152311", "11125132", "11134222", "11143312", "21116131", "21125221", "21134311", "12161131",
"11252131", "12143131", "12152221", "12161311", "11234131", "11243221", "11252311", "12125131", "12134221", "12143311", "11216131", "11225221", "11234311", "11111236", "31111234",
"51111232", "21111325", "41111323", "61111321", "11111416", "31111414", "51111412", "31211143", "51211141", "12111235", "32111233", "52111231", "21211234", "41211232", "22111324",
"42111322", "11211325", "31211323", "51211321", "12111415", "32111413", "52111411", "21211414", "41211412", "12211144", "32211142", "21311143", "41311141",
/* Column 8 */
"13111234", "33111232", "22211233", "42211231", "11311234", "31311232", "23111323", "43111321", "12211324", "32211322", "21311323", "41311321", "13111414", "33111412", "22211413",
"42211411", "11311414", "31311412", "13211143", "33211141", "22311142", "11411143", "31411141", "14111233", "34111231", "23211232", "12311233", "32311231", "21411232", "24111322",
"13211323", "33211321", "22311322", "11411323", "31411321", "14111413", "34111411", "23211412", "12311413", "32311411", "21411412", "14211142", "23311141", "12411142", "21511141",
"15111232", "24211231", "13311232", "22411231", "11511232", "25111321", "14211322", "23311321", "12411322", "21511321", "15111412", "24211411", "13311412",
/* Column 9 */
"22411411", "11511412", "15211141", "13411141", "11611141", "16111231", "14311231", "12511231", "15211321", "13411321", "11611321", "16111411", "14311411", "12511411", "21121144",
"41121142", "11112145", "31112143", "51112141", "11121235", "31121233", "51121231", "21112234", "41112232", "21121324", "41121322", "11112325", "31112323", "51112321", "11121415",
"31121413", "51121411", "21112414", "41112412", "22121143", "42121141", "11221144", "31221142", "12112144", "32112142", "12121234", "32121232", "21221233", "41221231", "22112233",
"42112231", "11212234", "22121323", "42121321", "11221324", "31221322", "12112324", "32112322", "12121414", "32121412", "21221413", "41221411", "22112413",
/* Column 10 */
"42112411", "11212414", "31212412", "23121142", "12221143", "32221141", "21321142", "13112143", "33112141", "13121233", "33121231", "11312143", "22221232", "11321233", "31321231",
"23112232", "12212233", "23121322", "12221323", "32221321", "21321322", "13112323", "33112321", "13121413", "33121411", "11312323", "22221412", "11321413", "31321411", "23112412",
"12212413", "32212411", "21312412", "24121141", "13221142", "22321141", "11421142", "14112142", "14121232", "12312142", "23221231", "12321232", "21421231", "24112231", "13212232",
"24121321", "13221322", "11412232", "22321321", "11421322", "14112322", "14121412", "12312322", "23221411", "12321412", "21421411", "24112411", "13212412",
/* Column 11 */
"22312411", "11412412", "14221141", "12421141", "15112141", "15121231", "13312141", "13321231", "11512141", "11521231", "14212231", "14221321", "12412231", "12421321", "15112321",
"15121411", "13312321", "13321411", "11512321", "11521411", "14212411", "12412411", "21131143", "41131141", "11122144", "31122142", "11131234", "31131232", "21113143", "41113141",
"21122233", "41122231", "21131323", "41131321", "11113234", "31113232", "11122324", "31122322", "11131414", "31131412", "21113323", "41113321", "21122413", "41122411", "11113414",
"31113412", "22131142", "11231143", "31231141", "12122143", "32122141", "12131233", "32131231", "21231232", "22113142", "11213143", "22122232", "11222233",
/* Column 12 */
"22131322", "11231323", "31231321", "12113233", "32113231", "12122323", "32122321", "12131413", "32131411", "21231412", "22113322", "11213323", "22122412", "11222413", "31222411",
"12113413", "32113411", "21213412", "23131141", "12231142", "21331141", "13122142", "13131232", "11322142", "22231231", "11331232", "23113141", "12213142", "23122231", "12222232",
"23131321", "12231322", "21331321", "13113232", "13122322", "11313232", "13131412", "11322322", "22231411", "11331412", "23113321", "12213322", "23122411", "12222412", "21322411",
"13113412", "22213411", "11313412", "13231141", "11431141", "14122141", "14131231", "12322141", "12331231", "13213141", "13222231", "11413141", "13231321",
/* Column 13 */
"11422231", "11431321", "14113231", "14122321", "12313231", "14131411", "12322321", "12331411", "13213321", "13222411", "11413321", "11422411", "14113411", "12313411", "21141142",
"11132143", "31132141", "11141233", "31141231", "21123142", "21132232", "21141322", "11114143", "31114141", "11123233", "31123231", "11132323", "31132321", "11141413", "31141411",
"21114232", "21123322", "21132412", "11114323", "31114321", "11123413", "31123411", "22141141", "11241142", "12132142", "12141232", "21241231", "22123141", "11223142", "22132231",
"11232232", "22141321", "11241322", "12114142", "12123232", "12132322", "12141412", "21241411", "22114231", "11214232", "22123321", "11223322", "22132411",
/* Column 14 */
"11232412", "12114322", "12123412", "21223411", "12241141", "13132141", "13141231", "11332141", "11341231", "12223141", "12232231", "12241321", "13114141", "13123231", "11314141",
"13132321", "11323231", "13141411", "11332321", "11341411", "12214231", "12223321", "12232411", "13114321", "13123411", "11314321", "11323411", "21151141", "11142142", "11151232",
"21133141", "21142231", "21151321", "11124142", "11133232", "11142322", "11151412", "21115141", "21124231", "21133321", "21142411", "11115232", "11124322", "11133412", "11251141",
"12142141", "12151231", "11233141", "11242231", "11251321", "12124141", "12133231", "12142321", "12151411", "11215141", "11224231", "11233321", "11242411",
/* Column 15 */
"12115231", "12124321", "12133411", "11152141", "11161231", "11134141", "11143231", "11152321", "11161411", "11116141", "11125231", "11134321", "11143411", "21111244", "41111242",
"11111335", "31111333", "51111331", "21111424", "41111422", "11111515", "31111513", "51111511", "21211153", "41211151", "22111243", "42111241", "11211244", "31211242", "12111334",
"32111332", "21211333", "41211331", "22111423", "42111421", "11211424", "31211422", "12111514", "32111512", "21211513", "41211511", "22211152", "11311153", "31311151", "23111242",
"12211243", "32211241", "21311242", "13111333", "33111331", "22211332", "11311333", "31311331", "23111422", "12211423", "32211421", "21311422", "13111513",
/* Column 16 */
"33111511", "22211512", "11311513", "31311511", "23211151", "12311152", "21411151", "24111241", "13211242", "22311241", "11411242", "14111332", "23211331", "12311332", "21411331",
"24111421", "13211422", "22311421", "11411422", "14111512", "23211511", "12311512", "21411511", "13311151", "11511151", "14211241", "12411241", "15111331", "13311331", "11511331",
"14211421", "12411421", "15111511", "13311511", "11511511", "31121152", "21112153", "41112151", "21121243", "41121241", "11112244", "31112242", "11121334", "31121332", "21112333",
"41112331", "21121423", "41121421", "11112424", "31112422", "11121514", "31121512", "21112513", "41112511", "12121153", "32121151", "21221152", "22112152",
/* Column 17 */
"11212153", "22121242", "11221243", "31221241", "12112243", "32112241", "12121333", "32121331", "21221332", "22112332", "11212333", "22121422", "11221423", "31221421", "12112423",
"32112421", "12121513", "32121511", "21221512", "22112512", "11212513", "31212511", "13121152", "22221151", "11321152", "23112151", "12212152", "23121241", "12221242", "21321241",
"13112242", "13121332", "11312242", "22221331", "11321332", "23112331", "12212332", "23121421", "12221422", "21321421", "13112422", "13121512", "11312422", "22221511", "11321512",
"23112511", "12212512", "21312511", "14121151", "12321151", "13212151", "13221241", "11412151", "11421241", "14112241", "14121331", "12312241", "12321331",
/* Column 18 */
"13212331", "13221421", "11412331", "11421421", "14112421", "14121511", "12312421", "12321511", "13212511", "11412511", "11131153", "31131151", "21122152", "21131242", "11113153",
"31113151", "11122243", "31122241", "11131333", "31131331", "21113242", "21122332", "21131422", "11113333", "31113331", "11122423", "31122421", "11131513", "31131511", "21113422",
"21122512", "12131152", "21231151", "22122151", "11222152", "22131241", "11231242", "12113152", "12122242", "12131332", "21231331", "22113241", "11213242", "22122331", "11222332",
"22131421", "11231422", "12113332", "12122422", "12131512", "21231511", "22113421", "11213422", "22122511", "11222512", "13131151", "11331151", "12222151",
/* Column 19 */
"12231241", "13113151", "13122241", "11313151", "13131331", "11322241", "11331331", "12213241", "12222331", "12231421", "13113331", "13122421", "11313331", "13131511", "11322421",
"11331511", "12213421", "12222511", "11141152", "21132151", "21141241", "11123152", "11132242", "11141332", "21114151", "21123241", "21132331", "21141421", "11114242", "11123332",
"11132422", "11141512", "21114331", "21123421", "21132511", "12141151", "11232151", "11241241", "12123151", "12132241", "12141331", "11214151", "11223241", "11232331", "11241421",
"12114241", "12123331", "12132421", "12141511", "11214331", "11223421", "11232511", "11151151", "11133151", "11142241", "11151331", "11115151", "11124241",
/* Column 20 */
"11133331", "11142421", "11151511", "11111254", "31111252", "21111343", "41111341", "11111434", "31111432", "21111523", "41111521", "11111614", "31111612", "31211161", "12111253",
"32111251", "21211252", "22111342", "11211343", "31211341", "12111433", "32111431", "21211432", "22111522", "11211523", "31211521", "12111613", "32111611", "21211612", "12211162",
"21311161", "13111252", "22211251", "11311252", "23111341", "12211342", "21311341", "13111432", "22211431", "11311432", "23111521", "12211522", "21311521", "13111612", "22211611",
"11311612", "13211161", "11411161", "14111251", "12311251", "13211341", "11411341", "14111431", "12311431", "13211521", "11411521", "14111611", "12311611",
/* Column 21 */
"21121162", "11112163", "31112161", "11121253", "31121251", "21112252", "21121342", "11112343", "31112341", "11121433", "31121431", "21112432", "21121522", "11112523", "31112521",
"11121613", "31121611", "22121161", "11221162", "12112162", "12121252", "21221251", "22112251", "11212252", "22121341", "11221342", "12112342", "12121432", "21221431", "22112431",
"11212432", "22121521", "11221522", "12112522", "12121612", "21221611", "12221161", "13112161", "13121251", "11312161", "11321251", "32121115", "52121113", "21221116", "41221114",
"61221112", "22112116", "42112114", "31212115", "51212113", "13121116", "33121114", "22221115", "42221113", "11321116", "31321114", "51321112", "23112115",
/* Column 22 */
"43112113", "12212116", "32212114", "52212112", "21312115", "41312113", "61312111", "14121115", "34121113", "23221114", "43221112", "12321115", "32321113", "52321111", "21421114",
"41421112", "24112114", "13212115", "33212113", "22312114", "42312112", "11412115", "31412113", "51412111", "15121114", "24221113", "13321114", "33321112", "22421113", "42421111",
"11521114", "31521112", "25112113", "14212114", "34212112", "23312113", "43312111", "12412114", "32412112", "21512113", "41512111", "16121113", "25221112", "14321113", "34321111",
"23421112", "12521113", "32521111", "15212113", "24312112", "13412113", "33412111", "22512112", "11612113", "31612111", "31131115", "51131113", "21122116",
/* Column 23 */
"41122114", "61122112", "31113115", "51113113", "12131116", "32131114", "52131112", "21231115", "41231113", "61231111", "22122115", "42122113", "11222116", "31222114", "51222112",
"12113116", "32113114", "52113112", "21213115", "41213113", "61213111", "13131115", "33131113", "22231114", "42231112", "11331115", "31331113", "51331111", "23122114", "43122112",
"12222115", "32222113", "52222111", "21322114", "41322112", "13113115", "33113113", "22213114", "42213112", "11313115", "31313113", "51313111", "14131114", "34131112", "23231113",
"43231111", "12331114", "32331112", "21431113", "41431111", "24122113", "13222114", "33222112", "22322113", "42322111", "11422114", "31422112", "14113114",
/* Column 24 */
"34113112", "23213113", "43213111", "12313114", "32313112", "21413113", "41413111", "15131113", "24231112", "13331113", "33331111", "22431112", "25122112", "14222113", "34222111",
"23322112", "12422113", "32422111", "21522112", "15113113", "24213112", "13313113", "33313111", "22413112", "11513113", "31513111", "16131112", "25231111", "14331112", "23431111",
"15222112", "24322111", "13422112", "22522111", "16113112", "25213111", "14313112", "23413111", "12513112", "21613111", "11141116", "31141114", "51141112", "21132115", "41132113",
"61132111", "11123116", "31123114", "51123112", "21114115", "41114113", "61114111", "12141115", "32141113", "52141111", "21241114", "41241112", "22132114",
/* Column 25 */
"42132112", "11232115", "31232113", "51232111", "12123115", "32123113", "52123111", "21223114", "41223112", "22114114", "42114112", "11214115", "31214113", "51214111", "13141114",
"33141112", "22241113", "42241111", "11341114", "31341112", "23132113", "43132111", "12232114", "32232112", "21332113", "41332111", "13123114", "33123112", "22223113", "42223111",
"11323114", "31323112", "23114113", "43114111", "12214114", "32214112", "21314113", "41314111", "14141113", "34141111", "23241112", "12341113", "32341111", "24132112", "13232113",
"33232111", "22332112", "11432113", "31432111", "14123113", "34123111", "23223112", "12323113", "32323111", "21423112", "24114112", "13214113", "33214111",
/* Column 26 */
"22314112", "11414113", "31414111", "15141112", "24241111", "13341112", "25132111", "14232112", "23332111", "12432112", "15123112", "24223111", "13323112", "22423111", "11523112",
"25114111", "14214112", "23314111", "12414112", "21514111", "16141111", "14341111", "15232111", "13432111", "16123111", "14323111", "12523111", "15214111", "13414111", "11614111",
"11151115", "31151113", "51151111", "21142114", "41142112", "11133115", "31133113", "51133111", "21124114", "41124112", "11115115", "31115113", "51115111", "12151114", "32151112",
"21251113", "41251111", "22142113", "42142111", "11242114", "31242112", "12133114", "32133112", "21233113", "41233111", "22124113", "42124111", "11224114",
/* Column 27 */
"31224112", "12115114", "32115112", "21215113", "41215111", "13151113", "33151111", "22251112", "23142112", "12242113", "32242111", "21342112", "13133113", "33133111", "22233112",
"11333113", "31333111", "23124112", "12224113", "32224111", "21324112", "13115113", "33115111", "22215112", "11315113", "31315111", "14151112", "23251111", "24142111", "13242112",
"22342111", "14133112", "23233111", "12333112", "21433111", "24124111", "13224112", "22324111", "11424112", "14115112", "23215111", "12315112", "21415111", "15151111", "14242111",
"15133111", "13333111", "14224111", "12424111", "15115111", "13315111", "11515111", "11161114", "31161112", "21152113", "41152111", "11143114", "31143112",
/* Column 28 */
"21134113", "41134111", "11125114", "31125112", "21116113", "41116111", "12161113", "32161111", "22152112", "11252113", "31252111", "12143113", "32143111", "21243112", "22134112",
"11234113", "31234111", "12125113", "32125111", "21225112", "22116112", "11216113", "31216111", "13161112", "23152111", "12252112", "13143112", "22243111", "11343112", "23134111",
"12234112", "21334111", "13125112", "22225111", "11325112", "23116111", "12216112", "21316111", "14161111", "13252111", "14143111", "12343111", "13234111", "11434111", "14125111",
"12325111", "13216111", "11416111", "31111216", "51111214", "31211125", "51211123", "32111215", "52111213", "21211216", "41211214", "61211212", "12211126",
/* Column 29 */
"32211124", "52211122", "21311125", "41311123", "61311121", "13111216", "33111214", "22211215", "42211213", "11311216", "31311214", "51311212", "13211125", "33211123", "22311124",
"42311122", "11411125", "31411123", "51411121", "14111215", "34111213", "23211214", "43211212", "12311215", "32311213", "52311211", "21411214", "41411212", "14211124", "34211122",
"23311123", "43311121", "12411124", "32411122", "21511123", "41511121", "15111214", "24211213", "13311214", "33311212", "22411213", "42411211", "11511214", "31511212", "15211123",
"24311122", "13411123", "33411121", "22511122", "11611123", "31611121", "16111213", "25211212", "14311213", "34311211", "23411212", "12511213", "32511211",
/* Column 30 */
"21611212", "21121126", "41121124", "61121122", "31112125", "51112123", "31121215", "51121213", "21112216", "41112214", "61112212", "22121125", "42121123", "11221126", "31221124",
"51221122", "12112126", "32112124", "52112122", "12121216", "32121214", "52121212", "21221215", "41221213", "61221211", "22112215", "42112213", "11212216", "31212214", "51212212",
"23121124", "43121122", "12221125", "32221123", "52221121", "21321124", "41321122", "13112125", "33112123", "13121215", "33121213", "11312125", "22221214", "42221212", "11321215",
"31321213", "51321211", "23112214", "43112212", "12212215", "32212213", "52212211", "21312214", "41312212", "24121123", "13221124", "33221122", "22321123",
/* Column 31 */
"42321121", "11421124", "31421122", "14112124", "34112122", "14121214", "34121212", "12312124", "23221213", "43221211", "12321214", "32321212", "21421213", "41421211", "24112213",
"13212214", "33212212", "22312213", "42312211", "11412214", "31412212", "25121122", "14221123", "34221121", "23321122", "12421123", "32421121", "21521122", "15112123", "15121213",
"13312123", "24221212", "13321213", "33321211", "11512123", "22421212", "11521213", "31521211", "25112212", "14212213", "34212211", "23312212", "12412213", "32412211", "21512212",
"15221122", "24321121", "13421122", "22521121", "16112122", "16121212", "14312122", "25221211", "14321212", "12512122", "23421211", "12521212", "15212212",
/* Column 32 */
"24312211", "13412212", "22512211", "11612212", "21131125", "41131123", "61131121", "11122126", "31122124", "51122122", "11131216", "31131214", "51131212", "21113125", "41113123",
"61113121", "21122215", "41122213", "61122211", "11113216", "31113214", "51113212", "22131124", "42131122", "11231125", "31231123", "51231121", "12122125", "32122123", "52122121",
"12131215", "32131213", "52131211", "21231214", "41231212", "22113124", "42113122", "11213125", "22122214", "42122212", "11222215", "31222213", "51222211", "12113215", "32113213",
"52113211", "21213214", "41213212", "23131123", "43131121", "12231124", "32231122", "21331123", "41331121", "13122124", "33122122", "13131214", "33131212",
/* Column 33 */
"11322124", "22231213", "42231211", "11331214", "31331212", "23113123", "43113121", "12213124", "23122213", "43122211", "12222214", "32222212", "21322213", "41322211", "13113214",
"33113212", "22213213", "42213211", "11313214", "31313212", "24131122", "13231123", "33231121", "22331122", "11431123", "31431121", "14122123", "34122121", "14131213", "34131211",
"12322123", "23231212", "12331213", "32331211", "21431212", "24113122", "13213123", "24122212", "13222213", "33222211", "11413123", "22322212", "11422213", "31422211", "14113213",
"34113211", "23213212", "12313213", "32313211", "21413212", "25131121", "14231122", "23331121", "12431122", "15122122", "15131212", "13322122", "24231211",
/* Column 34 */
"13331212", "11522122", "22431211", "25113121", "14213122", "25122211", "14222212", "12413122", "23322211", "12422212", "21522211", "15113212", "24213211", "13313212", "22413211",
"11513212", "15231121", "13431121", "16122121", "16131211", "14322121", "14331211", "12522121", "15213121", "15222211", "13413121", "13422211", "11613121", "16113211", "14313211",
"12513211", "21141124", "41141122", "11132125", "31132123", "51132121", "11141215", "31141213", "51141211", "21123124", "41123122", "21132214", "41132212", "11114125", "31114123",
"51114121", "11123215", "31123213", "51123211", "21114214", "41114212", "22141123", "42141121", "11241124", "31241122", "12132124", "32132122", "12141214",
/* Column 35 */
"32141212", "21241213", "41241211", "22123123", "42123121", "11223124", "22132213", "42132211", "11232214", "31232212", "12114124", "32114122", "12123214", "32123212", "21223213",
"41223211", "22114213", "42114211", "11214214", "31214212", "23141122", "12241123", "32241121", "21341122", "13132123", "33132121", "13141213", "33141211", "11332123", "22241212",
"11341213", "31341211", "23123122", "12223123", "23132212", "12232213", "32232211", "21332212", "13114123", "33114121", "13123213", "33123211", "11314123", "22223212", "11323213",
"31323211", "23114212", "12214213", "32214211", "21314212", "24141121", "13241122", "22341121", "14132122", "14141212", "12332122", "23241211", "12341212",
/* Column 36 */
"24123121", "13223122", "24132211", "13232212", "11423122", "22332211", "11432212", "14114122", "14123212", "12314122", "23223211", "12323212", "21423211", "24114211", "13214212",
"22314211", "11414212", "14241121", "15132121", "15141211", "13332121", "13341211", "14223121", "14232211", "12423121", "12432211", "15114121", "15123211", "13314121", "13323211",
"11514121", "11523211", "14214211", "12414211", "21151123", "41151121", "11142124", "31142122", "11151214", "31151212", "21133123", "41133121", "21142213", "41142211", "11124124",
"31124122", "11133214", "31133212", "21115123", "41115121", "21124213", "41124211", "11115214", "31115212", "22151122", "11251123", "31251121", "12142123",
/* Column 37 */
"32142121", "12151213", "32151211", "21251212", "22133122", "11233123", "22142212", "11242213", "31242211", "12124123", "32124121", "12133213", "32133211", "21233212", "22115122",
"11215123", "22124212", "11224213", "31224211", "12115213", "32115211", "21215212", "23151121", "12251122", "13142122", "13151212", "11342122", "22251211", "23133121", "12233122",
"23142211", "12242212", "21342211", "13124122", "13133212", "11324122", "22233211", "11333212", "23115121", "12215122", "23124211", "12224212", "21324211", "13115212", "22215211",
"11315212", "13251121", "14142121", "14151211", "12342121", "13233121", "13242211", "11433121", "14124121", "14133211", "12324121", "12333211", "13215121",
/* Column 38 */
"13224211", "11415121", "11424211", "14115211", "12315211", "21161122", "11152123", "31152121", "11161213", "31161211", "21143122", "21152212", "11134123", "31134121", "11143213",
"31143211", "21125122", "21134212", "11116123", "31116121", "11125213", "31125211", "22161121", "12152122", "12161212", "22143121", "11243122", "22152211", "11252212", "12134122",
"12143212", "21243211", "22125121", "11225122", "22134211", "11234212", "12116122", "12125212", "21225211", "13152121", "13161211", "12243121", "12252211", "13134121", "13143211",
"11334121", "11343211", "12225121", "12234211", "13116121", "13125211", "11316121", "11325211", "21111226", "41111224", "61111222", "31111315", "51111313",
/* Column 39 */
"21211135", "41211133", "61211131", "22111225", "42111223", "11211226", "31211224", "51211222", "12111316", "32111314", "52111312", "21211315", "41211313", "61211311", "22211134",
"42211132", "11311135", "31311133", "51311131", "23111224", "43111222", "12211225", "32211223", "52211221", "21311224", "41311222", "13111315", "33111313", "22211314", "42211312",
"11311315", "31311313", "51311311", "23211133", "43211131", "12311134", "32311132", "21411133", "41411131", "24111223", "13211224", "33211222", "22311223", "42311221", "11411224",
"31411222", "14111314", "34111312", "23211313", "43211311", "12311314", "32311312", "21411313", "41411311", "24211132", "13311133", "33311131", "22411132",
/* Column 40 */
"11511133", "31511131", "25111222", "14211223", "34211221", "23311222", "12411223", "32411221", "21511222", "15111313", "24211312", "13311313", "33311311", "22411312", "11511313",
"31511311", "25211131", "14311132", "23411131", "12511132", "21611131", "15211222", "24311221", "13411222", "22511221", "11611222", "16111312", "25211311", "14311312", "23411311",
"12511312", "21611311", "31121134", "51121132", "21112135", "41112133", "61112131", "21121225", "41121223", "61121221", "11112226", "31112224", "51112222", "11121316", "31121314",
"51121312", "21112315", "41112313", "61112311", "12121135", "32121133", "52121131", "21221134", "41221132", "22112134", "42112132", "11212135", "22121224",
/* Column 41 */
"42121222", "11221225", "31221223", "51221221", "12112225", "32112223", "52112221", "12121315", "32121313", "52121311", "21221314", "41221312", "22112314", "42112312", "11212315",
"31212313", "51212311", "13121134", "33121132", "22221133", "42221131", "11321134", "31321132", "23112133", "43112131", "12212134", "23121223", "43121221", "12221224", "32221222",
"21321223", "41321221", "13112224", "33112222", "13121314", "33121312", "11312224", "22221313", "42221311", "11321314", "31321312",
/* Column 42 */
"23112313", "43112311", "12212314", "32212312", "21312313", "41312311", "14121133", "34121131", "23221132", "12321133", "32321131", "21421132", "24112132", "13212133", "24121222",
"13221223", "33221221", "11412133", "22321222", "11421223", "31421221", "14112223", "34112221", "14121313", "34121311", "12312223", "23221312", "12321313", "32321311", "21421312",
"24112312", "13212313", "33212311", "22312312", "11412313", "31412311", "15121132", "24221131", "13321132", "22421131" };
 
private static final String[] C49_APPXE_ODD = {
/* Appendix E - Code 49 Encodation Patterns (Odd Symbol Character Parity) */
/* Column 1 */
"22121116", "42121114", "31221115", "51221113", "32112115", "52112113", "21212116", "41212114", "61212112", "23121115", "43121113", "12221116", "32221114", "52221112", "21321115",
"41321113", "61321111", "13112116", "33112114", "22212115", "42212113", "11312116", "31312114", "51312112", "24121114", "13221115", "33221113", "22321114", "42321112", "11421115",
"31421113", "51421111", "14112115", "34112113", "23212114", "43212112", "12312115", "32312113", "52312111", "21412114", "41412112", "25121113", "14221114", "34221112", "23321113",
"43321111", "12421114", "32421112", "21521113", "41521111", "15112114", "24212113", "13312114", "33312112", "22412113", "42412111", "11512114", "31512112",
/* Column 2 */
"15221113", "24321112", "13421113", "33421111", "22521112", "16112113", "25212112", "14312113", "34312111", "23412112", "12512113", "32512111", "21612112", "21131116", "41131114",
"61131112", "31122115", "51122113", "21113116", "41113114", "61113112", "22131115", "42131113", "11231116", "31231114", "51231112", "12122116", "32122114", "52122112", "21222115",
"41222113", "61222111", "22113115", "42113113", "11213116", "31213114", "51213112", "23131114", "43131112", "12231115", "32231113", "52231111", "21331114", "41331112", "13122115",
"33122113", "22222114", "42222112", "11322115", "31322113", "51322111", "23113114", "43113112", "12213115", "32213113", "52213111", "21313114", "41313112",
/* Column 3 */
"24131113", "13231114", "33231112", "22331113", "42331111", "11431114", "31431112", "14122114", "34122112", "23222113", "43222111", "12322114", "32322112", "21422113", "41422111",
"24113113", "13213114", "33213112", "22313113", "42313111", "11413114", "31413112", "25131112", "14231113", "34231111", "23331112", "12431113", "32431111", "15122113", "24222112",
"13322113", "33322111", "22422112", "11522113", "31522111", "25113112", "14213113", "34213111", "23313112", "12413113", "32413111", "21513112", "15231112", "24331111", "13431112",
"16122112", "25222111", "14322112", "23422111", "12522112", "15213112", "24313111", "13413112", "22513111", "11613112", "21141115", "41141113", "61141111",
/* Column 4 */
"11132116", "31132114", "51132112", "21123115", "41123113", "61123111", "11114116", "31114114", "51114112", "22141114", "42141112", "11241115", "31241113", "51241111", "12132115",
"32132113", "52132111", "21232114", "41232112", "22123114", "42123112", "11223115", "31223113", "51223111", "12114115", "32114113", "52114111", "21214114", "41214112", "23141113",
"43141111", "12241114", "32241112", "21341113", "41341111", "13132114", "33132112", "22232113", "42232111", "11332114", "31332112", "23123113", "43123111", "12223114", "32223112",
"21323113", "41323111", "13114114", "33114112", "22214113", "42214111", "11314114", "31314112", "24141112", "13241113", "33241111", "22341112", "14132113",
/* Column 5 */
"34132111", "23232112", "12332113", "32332111", "21432112", "24123112", "13223113", "33223111", "22323112", "11423113", "31423111", "14114113", "34114111", "23214112", "12314113",
"32314111", "21414112", "25141111", "14241112", "23341111", "15132112", "24232111", "13332112", "22432111", "25123111", "14223112", "23323111", "12423112", "21523111", "15114112",
"24214111", "13314112", "22414111", "11514112", "15241111", "16132111", "14332111", "15223111", "13423111", "16114111", "14314111", "12514111", "21151114", "41151112", "11142115",
"31142113", "51142111", "21133114", "41133112", "11124115", "31124113", "51124111", "21115114", "41115112", "22151113", "42151111", "11251114", "31251112",
/* Column 6 */
"12142114", "32142112", "21242113", "41242111", "22133113", "42133111", "11233114", "31233112", "12124114", "32124112", "21224113", "41224111", "22115113", "42115111", "11215114",
"31215112", "23151112", "12251113", "32251111", "13142113", "33142111", "22242112", "11342113", "31342111", "23133112", "12233113", "32233111", "21333112", "13124113", "33124111",
"22224112", "11324113", "31324111", "23115112", "12215113", "32215111", "21315112", "24151111", "13251112", "14142112", "23242111", "12342112", "24133111", "13233112", "22333111",
"11433112", "14124112", "23224111", "12324112", "21424111", "24115111", "13215112", "22315111", "11415112", "14251111", "15142111", "13342111", "14233111",
/* Column 7 */
"12433111", "15124111", "13324111", "11524111", "14215111", "12415111", "21161113", "41161111", "11152114", "31152112", "21143113", "41143111", "11134114", "31134112", "21125113",
"41125111", "11116114", "31116112", "22161112", "12152113", "32152111", "21252112", "22143112", "11243113", "31243111", "12134113", "32134111", "21234112", "22125112", "11225113",
"31225111", "12116113", "32116111", "21216112", "23161111", "13152112", "22252111", "23143111", "12243112", "21343111", "13134112", "22234111", "11334112", "23125111", "12225112",
"21325111", "13116112", "22216111", "11316112", "14152111", "13243111", "14134111", "12334111", "13225111", "11425111", "14116111", "12316111", "41111215",
/* Column 8 */
"61111213", "21211126", "41211124", "61211122", "22111216", "42111214", "31211215", "51211213", "22211125", "42211123", "11311126", "31311124", "51311122", "23111215", "43111213",
"12211216", "32211214", "52211212", "21311215", "41311213", "61311211", "23211124", "43211122", "12311125", "32311123", "52311121", "21411124", "41411122", "24111214", "13211215",
"33211213", "22311214", "42311212", "11411215", "31411213", "51411211", "24211123", "13311124", "33311122", "22411123", "42411121", "11511124", "31511122", "25111213", "14211214",
"34211212", "23311213", "43311211", "12411214", "32411212", "21511213", "41511211", "25211122", "14311123", "34311121", "23411122", "12511123", "32511121",
/* Column 9 */
"21611122", "15211213", "24311212", "13411213", "33411211", "22511212", "11611213", "31611211", "31121125", "51121123", "21112126", "41112124", "61112122", "21121216", "41121214",
"61121212", "31112215", "51112213", "12121126", "32121124", "52121122", "21221125", "41221123", "61221121", "22112125", "42112123", "11212126", "22121215", "42121213", "11221216",
"31221214", "51221212", "12112216", "32112214", "52112212", "21212215", "41212213", "61212211", "13121125", "33121123", "22221124", "42221122", "11321125", "31321123", "51321121",
"23112124", "43112122", "12212125", "23121214", "43121212", "12221215", "32221213", "52221211", "21321214", "41321212", "13112215", "33112213", "22212214",
/* Column 10 */
"42212212", "11312215", "31312213", "51312211", "14121124", "34121122", "23221123", "43221121", "12321124", "32321122", "21421123", "41421121", "24112123", "13212124", "24121213",
"13221214", "33221212", "11412124", "22321213", "42321211", "11421214", "31421212", "14112214", "34112212", "23212213", "43212211", "12312214", "32312212", "21412213", "41412211",
"15121123", "24221122", "13321123", "33321121", "22421122", "11521123", "31521121", "25112122", "14212123", "25121212", "14221213", "34221211", "12412123", "23321212", "12421213",
"32421211", "21521212", "15112213", "24212212", "13312213", "33312211", "22412212", "11512213", "31512211", "16121122", "25221121", "14321122", "23421121",
/* Column 11 */
"12521122", "15212122", "15221212", "13412122", "24321211", "13421212", "11612122", "22521211", "16112212", "25212211", "14312212", "23412211", "12512212", "21612211", "11131126",
"31131124", "51131122", "21122125", "41122123", "61122121", "21131215", "41131213", "61131211", "11113126", "31113124", "51113122", "11122216", "31122214", "51122212", "21113215",
"41113213", "61113211", "12131125", "32131123", "52131121", "21231124", "41231122", "22122124", "42122122", "11222125", "22131214", "42131212", "11231215", "31231213", "51231211",
"12113125", "32113123", "52113121", "12122215", "32122213", "52122211", "21222214", "41222212", "22113214", "42113212", "11213215", "31213213", "51213211",
/* Column 12 */
"13131124", "33131122", "22231123", "42231121", "11331124", "31331122", "23122123", "43122121", "12222124", "23131213", "43131211", "12231214", "32231212", "21331213", "41331211",
"13113124", "33113122", "13122214", "33122212", "11313124", "22222213", "42222211", "11322214", "31322212", "23113213", "43113211", "12213214", "32213212", "21313213", "41313211",
"14131123", "34131121", "23231122", "12331123", "32331121", "21431122", "24122122", "13222123", "24131212", "13231213", "33231211", "11422123", "22331212", "11431213", "31431211",
"14113123", "34113121", "14122213", "34122211", "12313123", "23222212", "12322213", "32322211", "21422212", "24113212", "13213213", "33213211", "22313212",
/* Column 13 */
"11413213", "31413211", "15131122", "24231121", "13331122", "22431121", "25122121", "14222122", "25131211", "14231212", "12422122", "23331211", "12431212", "15113122", "15122212",
"13313122", "24222211", "13322212", "11513122", "22422211", "11522212", "25113211", "14213212", "23313211", "12413212", "21513211", "16131121", "14331121", "15222121", "15231211",
"13422121", "13431211", "16113121", "16122211", "14313121", "14322211", "12513121", "12522211", "15213211", "13413211", "11613211", "11141125", "31141123", "51141121", "21132124",
"41132122", "21141214", "41141212", "11123125", "31123123", "51123121", "11132215", "31132213", "51132211", "21114124", "41114122", "21123214", "41123212",
/* Column 14 */
"11114215", "31114213", "51114211", "12141124", "32141122", "21241123", "41241121", "22132123", "42132121", "11232124", "22141213", "42141211", "11241214", "31241212", "12123124",
"32123122", "12132214", "32132212", "21232213", "41232211", "22114123", "42114121", "11214124", "22123213", "42123211", "11223214", "31223212", "12114214", "32114212", "21214213",
"41214211", "13141123", "33141121", "22241122", "11341123", "31341121", "23132122", "12232123", "23141212", "12241213", "32241211", "21341212", "13123123", "33123121", "13132213",
"33132211", "11323123", "22232212", "11332213", "31332211", "23114122", "12214123", "23123212", "12223213", "32223211", "21323212", "13114213", "33114211",
/* Column 15 */
"22214212", "11314213", "31314211", "14141122", "23241121", "12341122", "24132121", "13232122", "24141211", "13241212", "11432122", "22341211", "14123122", "14132212", "12323122",
"23232211", "12332212", "21432211", "24114121", "13214122", "24123211", "13223212", "11414122", "22323211", "11423212", "14114212", "23214211", "12314212", "21414211", "15141121",
"13341121", "14232121", "14241211", "12432121", "15123121", "15132211", "13323121", "13332211", "11523121", "14214121", "14223211", "12414121", "12423211", "15114211", "13314211",
"11514211", "11151124", "31151122", "21142123", "41142121", "21151213", "41151211", "11133124", "31133122", "11142214", "31142212", "21124123", "41124121",
/* Column 16 */
"21133213", "41133211", "11115124", "31115122", "11124214", "31124212", "21115213", "41115211", "12151123", "32151121", "21251122", "22142122", "11242123", "22151212", "11251213",
"31251211", "12133123", "32133121", "12142213", "32142211", "21242212", "22124122", "11224123", "22133212", "11233213", "31233211", "12115123", "32115121", "12124213", "32124211",
"21224212", "22115212", "11215213", "31215211", "13151122", "22251121", "23142121", "12242122", "23151211", "12251212", "13133122", "13142212", "11333122", "22242211", "11342212",
"23124121", "12224122", "23133211", "12233212", "21333211", "13115122", "13124212", "11315122", "22224211", "11324212", "23115211", "12215212", "21315211",
/* Column 17 */
"14151121", "13242121", "13251211", "14133121", "14142211", "12333121", "12342211", "13224121", "13233211", "11424121", "11433211", "14115121", "14124211", "12315121", "12324211",
"13215211", "11415211", "11161123", "31161121", "21152122", "21161212", "11143123", "31143121", "11152213", "31152211", "21134122", "21143212", "11125123", "31125121", "11134213",
"31134211", "21116122", "21125212", "12161122", "22152121", "11252122", "22161211", "12143122", "12152212", "21252211", "22134121", "11234122", "22143211", "11243212", "12125122",
"12134212", "21234211", "22116121", "11216122", "22125211", "11225212", "13161121", "12252121", "13143121", "13152211", "11343121", "12234121", "12243211",
/* Column 18 */
"13125121", "13134211", "11325121", "11334211", "12216121", "12225211", "31111225", "51111223", "21111316", "41111314", "61111312", "31211134", "51211132", "12111226", "32111224",
"52111222", "21211225", "41211223", "61211221", "22111315", "42111313", "11211316", "31211314", "51211312", "12211135", "32211133", "52211131", "21311134", "41311132", "13111225",
"33111223", "22211224", "42211222", "11311225", "31311223", "51311221", "23111314", "43111312", "12211315", "32211313", "52211311", "21311314", "41311312", "13211134", "33211132",
"22311133", "42311131", "11411134", "31411132", "14111224", "34111222", "23211223", "43211221", "12311224", "32311222", "21411223", "41411221", "24111313",
/* Column 19 */
"13211314", "33211312", "22311313", "42311311", "11411314", "31411312", "14211133", "34211131", "23311132", "12411133", "32411131", "21511132", "15111223", "24211222", "13311223",
"33311221", "22411222", "11511223", "31511221", "25111312", "14211313", "34211311", "23311312", "12411313", "32411311", "21511312", "15211132", "24311131", "13411132", "22511131",
"11611132", "16111222", "25211221", "14311222", "23411221", "12511222", "21611221", "15211312", "24311311", "13411312", "22511311", "11611312", "21121135", "41121133", "61121131",
"11112136", "31112134", "51112132", "11121226", "31121224", "51121222", "21112225", "41112223", "61112221", "21121315", "41121313", "61121311", "11112316",
/* Column 20 */
"31112314", "51112312", "22121134", "42121132", "11221135", "31221133", "51221131", "12112135", "32112133", "52112131", "12121225", "32121223", "52121221", "21221224", "41221222",
"22112224", "42112222", "11212225", "22121314", "42121312", "11221315", "31221313", "51221311", "12112315", "32112313", "52112311", "21212314", "41212312", "23121133", "43121131",
"12221134", "32221132", "21321133", "41321131", "13112134", "33112132", "13121224", "33121222", "11312134", "22221223", "42221221", "11321224", "31321222", "23112223", "43112221",
"12212224", "23121313", "43121311", "12221314", "32221312", "21321313", "41321311", "13112314", "33112312", "22212313", "42212311", "11312314", "31312312",
/* Column 21 */
"24121132", "13221133", "33221131", "22321132", "11421133", "31421131", "14112133", "34112131", "14121223", "34121221", "12312133", "23221222", "12321223", "32321221", "21421222",
"24112222", "13212223", "24121312", "13221313", "33221311", "11412223", "22321312", "11421313", "31421311", "14112313", "34112311", "23212312", "12312313", "32312311", "21412312",
"25121131", "14221132", "23321131", "12421132", "21521131", "15112132", "15121222", "13312132", "24221221", "13321222", "11512132", "22421221", "11521222", "25112221", "14212222",
"25121311", "14221312", "12412222", "23321311", "12421312", "21521311", "15112312", "24212311", "13312312", "22412311", "11512312", "15221131", "13421131",
/* Column 22 */
"16112131", "16121221", "14312131", "14321221", "12512131", "12521221", "15212221", "15221311", "13412221", "13421311", "11612221", "16112311", "14312311", "12512311", "21131134",
"41131132", "11122135", "31122133", "51122131", "11131225", "31131223", "51131221", "21113134", "41113132", "21122224", "41122222", "21131314", "41131312", "11113225", "31113223",
"51113221", "11122315", "31122313", "51122311", "21113314", "41113312", "22131133", "42131131", "11231134", "31231132", "12122134", "32122132", "12131224", "32131222", "21231223",
"41231221", "22113133", "42113131", "11213134", "22122223", "42122221", "11222224", "22131313", "42131311", "11231314", "31231312", "12113224", "32113222",
/* Column 23 */
"12122314", "32122312", "21222313", "41222311", "22113313", "42113311", "11213314", "31213312", "23131132", "12231133", "32231131", "21331132", "13122133", "33122131", "13131223",
"33131221", "11322133", "22231222", "11331223", "31331221", "23113132", "12213133", "23122222", "12222223", "23131312", "12231313", "32231311", "21331312", "13113223", "33113221",
"13122313", "33122311", "11313223", "22222312", "11322313", "31322311", "23113312", "12213313", "32213311", "21313312", "24131131", "13231132", "22331131", "11431132", "14122132",
"14131222", "12322132", "23231221", "12331222", "21431221", "24113131", "13213132", "24122221", "13222222", "24131311", "11413132", "13231312", "11422222",
/* Column 24 */
"22331311", "11431312", "14113222", "14122312", "12313222", "23222311", "12322312", "21422311", "24113311", "13213312", "22313311", "11413312", "14231131", "12431131", "15122131",
"15131221", "13322131", "13331221", "11522131", "14213131", "14222221", "12413131", "14231311", "12422221", "12431311", "15113221", "15122311", "13313221", "13322311", "11513221",
"11522311", "14213311", "12413311", "21141133", "41141131", "11132134", "31132132", "11141224", "31141222", "21123133", "41123131", "21132223", "41132221", "21141313", "41141311",
"11114134", "31114132", "11123224", "31123222", "11132314", "31132312", "21114223", "41114221", "21123313", "41123311", "11114314", "31114312", "22141132",
/* Column 25 */
"11241133", "31241131", "12132133", "32132131", "12141223", "32141221", "21241222", "22123132", "11223133", "22132222", "11232223", "22141312", "11241313", "31241311", "12114133",
"32114131", "12123223", "32123221", "12132313", "32132311", "21232312", "22114222", "11214223", "22123312", "11223313", "31223311", "12114313", "32114311", "21214312", "23141131",
"12241132", "21341131", "13132132", "13141222", "11332132", "22241221", "11341222", "23123131", "12223132", "23132221", "12232222", "23141311", "12241312", "21341311", "13114132",
"13123222", "11314132", "13132312", "11323222", "22232311", "11332312", "23114221", "12214222", "23123311", "12223312", "21323311", "13114312", "22214311",
/* Column 26 */
"11314312", "13241131", "14132131", "14141221", "12332131", "12341221", "13223131", "13232221", "11423131", "13241311", "11432221", "14114131", "14123221", "12314131", "14132311",
"12323221", "12332311", "13214221", "13223311", "11414221", "11423311", "14114311", "12314311", "21151132", "11142133", "31142131", "11151223", "31151221", "21133132", "21142222",
"21151312", "11124133", "31124131", "11133223", "31133221", "11142313", "31142311", "21115132", "21124222", "21133312", "11115223", "31115221", "11124313", "31124311", "22151131",
"11251132", "12142132", "12151222", "21251221", "22133131", "11233132", "22142221", "11242222", "22151311", "11251312", "12124132", "12133222", "12142312",
/* Column 27 */
"21242311", "22115131", "11215132", "22124221", "11224222", "22133311", "11233312", "12115222", "12124312", "21224311", "12251131", "13142131", "13151221", "11342131", "12233131",
"12242221", "12251311", "13124131", "13133221", "11324131", "13142311", "11333221", "11342311", "12215131", "12224221", "12233311", "13115221", "13124311", "11315221", "11324311",
"21161131", "11152132", "11161222", "21143131", "21152221", "21161311", "11134132", "11143222", "11152312", "21125131", "21134221", "21143311", "11116132", "11125222", "11134312",
"12152131", "12161221", "11243131", "11252221", "12134131", "12143221", "12152311", "11225131", "11234221", "11243311", "12116131", "12125221", "12134311",
/* Column 28 */
"21111235", "41111233", "61111231", "11111326", "31111324", "51111322", "21111415", "41111413", "61111411", "21211144", "41211142", "22111234", "42111232", "11211235", "31211233",
"51211231", "12111325", "32111323", "52111321", "21211324", "41211322", "22111414", "42111412", "11211415", "31211413", "51211411", "22211143", "42211141", "11311144", "31311142",
"23111233", "43111231", "12211234", "32211232", "21311233", "41311231", "13111324", "33111322", "22211323", "42211321", "11311324", "31311322", "23111413", "43111411", "12211414",
"32211412", "21311413", "41311411", "23211142", "12311143", "32311141", "21411142", "24111232", "13211233", "33211231", "22311232", "11411233", "31411231",
/* Column 29 */
"14111323", "34111321", "23211322", "12311323", "32311321", "21411322", "24111412", "13211413", "33211411", "22311412", "11411413", "31411411", "24211141", "13311142", "22411141",
"11511142", "25111231", "14211232", "23311231", "12411232", "21511231", "15111322", "24211321", "13311322", "22411321", "11511322", "25111411", "14211412", "23311411", "12411412",
"21511411", "14311141", "12511141", "15211231", "13411231", "11611231", "16111321", "14311321", "12511321", "15211411", "13411411", "11611411", "31121143", "51121141", "21112144",
"41112142", "21121234", "41121232", "11112235", "31112233", "51112231", "11121325", "31121323", "51121321", "21112324", "41112322", "21121414", "41121412",
/* Column 30 */
"11112415", "31112413", "51112411", "12121144", "32121142", "21221143", "41221141", "22112143", "42112141", "11212144", "22121233", "42121231", "11221234", "31221232", "12112234",
"32112232", "12121324", "32121322", "21221323", "41221321", "22112323", "42112321", "11212324", "22121413", "42121411", "11221414", "31221412", "12112414", "32112412", "21212413",
"41212411", "13121143", "33121141", "22221142", "11321143", "31321141", "23112142", "12212143", "23121232", "12221233", "32221231", "21321232", "13112233", "33112231", "13121323",
"33121321", "11312233", "22221322", "11321323", "31321321", "23112322", "12212323", "23121412", "12221413", "32221411", "21321412", "13112413", "33112411",
/* Column 31 */
"22212412", "11312413", "31312411", "14121142", "23221141", "12321142", "21421141", "24112141", "13212142", "24121231", "13221232", "11412142", "22321231", "11421232", "14112232",
"14121322", "12312232", "23221321", "12321322", "21421321", "24112321", "13212322", "24121411", "13221412", "11412322", "22321411", "11421412", "14112412", "23212411", "12312412",
"21412411", "15121141", "13321141", "11521141", "14212141", "14221231", "12412141", "12421231", "15112231", "15121321", "13312231", "13321321", "11512231", "11521321", "14212321",
"14221411", "12412321", "12421411", "15112411", "13312411", "11512411", "11131144", "31131142", "21122143", "41122141", "21131233", "41131231", "11113144",
/* Column 32 */
"31113142", "11122234", "31122232", "11131324", "31131322", "21113233", "41113231", "21122323", "41122321", "21131413", "41131411", "11113324", "31113322", "11122414", "31122412",
"21113413", "41113411", "12131143", "32131141", "21231142", "22122142", "11222143", "22131232", "11231233", "31231231", "12113143", "32113141", "12122233", "32122231", "12131323",
"32131321", "21231322", "22113232", "11213233", "22122322", "11222323", "22131412", "11231413", "31231411", "12113323", "32113321", "12122413", "32122411", "21222412", "22113412",
"11213413", "31213411", "13131142", "22231141", "11331142", "23122141", "12222142", "23131231", "12231232", "21331231", "13113142", "13122232", "11313142",
/* Column 33 */
"13131322", "11322232", "22231321", "11331322", "23113231", "12213232", "23122321", "12222322", "23131411", "12231412", "21331411", "13113322", "13122412", "11313322", "22222411",
"11322412", "23113411", "12213412", "21313411", "14131141", "12331141", "13222141", "13231231", "11422141", "11431231", "14113141", "14122231", "12313141", "14131321", "12322231",
"12331321", "13213231", "13222321", "11413231", "13231411", "11422321", "11431411", "14113321", "14122411", "12313321", "12322411", "13213411", "11413411", "11141143", "31141141",
"21132142", "21141232", "11123143", "31123141", "11132233", "31132231", "11141323", "31141321", "21114142", "21123232", "21132322", "21141412", "11114233",
/* Column 34 */
"31114231", "11123323", "31123321", "11132413", "31132411", "21114322", "21123412", "12141142", "21241141", "22132141", "11232142", "22141231", "11241232", "12123142", "12132232",
"12141322", "21241321", "22114141", "11214142", "22123231", "11223232", "22132321", "11232322", "22141411", "11241412", "12114232", "12123322", "12132412", "21232411", "22114321",
"11214322", "22123411", "11223412", "13141141", "11341141", "12232141", "12241231", "13123141", "13132231", "11323141", "13141321", "11332231", "11341321", "12214141", "12223231",
"12232321", "12241411", "13114231", "13123321", "11314231", "13132411", "11323321", "11332411", "12214321", "12223411", "11151142", "21142141", "21151231",
/* Column 35 */
"11133142", "11142232", "11151322", "21124141", "21133231", "21142321", "21151411", "11115142", "11124232", "11133322", "11142412", "21115231", "21124321", "21133411", "12151141",
"11242141", "11251231", "12133141", "12142231", "12151321", "11224141", "11233231", "11242321", "11251411", "12115141", "12124231", "12133321", "12142411", "11215231", "11224321",
"11233411", "11161141", "11143141", "11152231", "11161321", "11125141", "11134231", "11143321", "11152411", "11111245", "31111243", "51111241", "21111334", "41111332", "11111425",
"31111423", "51111421", "21111514", "41111512", "31211152", "12111244", "32111242", "21211243", "41211241", "22111333", "42111331", "11211334", "31211332",
/* Column 36 */
"12111424", "32111422", "21211423", "41211421", "22111513", "42111511", "11211514", "31211512", "12211153", "32211151", "21311152", "13111243", "33111241", "22211242", "11311243",
"31311241", "23111332", "12211333", "32211331", "21311332", "13111423", "33111421", "22211422", "11311423", "31311421", "23111512", "12211513", "32211511", "21311512", "13211152",
"22311151", "11411152", "14111242", "23211241", "12311242", "21411241", "24111331", "13211332", "22311331", "11411332", "14111422", "23211421", "12311422", "21411421", "24111511",
"13211512", "22311511", "11411512", "14211151", "12411151", "15111241", "13311241", "11511241", "14211331", "12411331", "15111421", "13311421", "11511421",
/* Column 37 */
"14211511", "12411511", "21121153", "41121151", "11112154", "31112152", "11121244", "31121242", "21112243", "41112241", "21121333", "41121331", "11112334", "31112332", "11121424",
"31121422", "21112423", "41112421", "21121513", "41121511", "11112514", "31112512", "22121152", "11221153", "31221151", "12112153", "32112151", "12121243", "32121241", "21221242",
"22112242", "11212243", "22121332", "11221333", "31221331", "12112333", "32112331", "12121423", "32121421", "21221422", "22112422", "11212423", "22121512", "11221513", "31221511",
"12112513", "32112511", "21212512", "23121151", "12221152", "21321151", "13112152", "13121242", "11312152", "22221241", "11321242", "23112241", "12212242",
/* Column 38 */
"23121331", "12221332", "21321331", "13112332", "13121422", "11312332", "22221421", "11321422", "23112421", "12212422", "23121511", "12221512", "21321511", "13112512", "22212511",
"11312512", "13221151", "11421151", "14112151", "14121241", "12312151", "12321241", "13212241", "13221331", "11412241", "11421331", "14112331", "14121421", "12312331", "12321421",
"13212421", "13221511", "11412421", "11421511", "14112511", "12312511", "21131152", "11122153", "31122151", "11131243", "31131241", "21113152", "21122242", "21131332", "11113243",
"31113241", "11122333", "31122331", "11131423", "31131421", "21113332", "21122422", "21131512", "11113423", "31113421", "11122513", "31122511", "22131151",
/* Column 39 */
"11231152", "12122152", "12131242", "21231241", "22113151", "11213152", "22122241", "11222242", "22131331", "11231332", "12113242", "12122332", "12131422", "21231421", "22113331",
"11213332", "22122421", "11222422", "22131511", "11231512", "12113422", "12122512", "21222511", "12231151", "13122151", "13131241", "11322151", "11331241", "12213151", "12222241",
"12231331", "13113241", "13122331", "11313241", "13131421", "11322331", "11331421", "12213331", "12222421", "12231511", "13113421", "13122511", "11313421", "11322511", "21141151",
"11132152", "11141242", "21123151", "21132241", "21141331", "11114152", "11123242", "11132332", "11141422", "21114241", "21123331", "21132421", "21141511",
/* Column 40 */
"11114332", "11123422", "11132512", "11241151", "12132151", "12141241", "11223151", "11232241", "11241331", "12114151", "12123241", "12132331", "12141421", "11214241", "11223331",
"11232421", "11241511", "12114331", "12123421", "12132511", "11142151", "11151241", "11124151", "11133241", "11142331", "11151421", "11115241", "11124331", "11133421", "11142511",
"21111253", "41111251", "11111344", "31111342", "21111433", "41111431", "11111524", "31111522", "21111613", "41111611", "21211162", "22111252", "11211253", "31211251", "12111343",
"32111341", "21211342", "22111432", "11211433", "31211431", "12111523", "32111521", "21211522", "22111612", "11211613", "31211611", "22211161", "11311162",
/* Column 41 */
"23111251", "12211252", "21311251", "13111342", "22211341", "11311342", "23111431", "12211432", "21311431", "13111522", "22211521", "11311522", "23111611", "12211612", "21311611",
"12311161", "13211251", "11411251", "14111341", "12311341", "13211431", "11411431", "14111521", "12311521", "13211611", "11411611", "31121161", "21112162", "21121252", "11112253",
"31112251", "11121343", "31121341", "21112342", "21121432", "11112433", "31112431", "11121523", "31121521", "21112522", "21121612",
/* Column 42 */
"12121162", "21221161", "22112161", "11212162", "22121251", "11221252", "12112252", "12121342", "21221341", "22112341", "11212342", "22121431", "11221432", "12112432", "12121522",
"21221521", "22112521", "11212522", "22121611", "11221612", "13121161", "11321161", "12212161", "12221251", "13112251", "13121341", "11312251", "11321341", "12212341", "12221431",
"13112431", "13121521", "11312431", "11321521", "12212521", "12221611", "11131162", "21122161", "21131251", "11113162" };
 
private static final char[] C49_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%', '!', '&', '*' };
 
@Override
protected boolean gs1Supported() {
return true;
}
 
@Override
protected void encode() {
int i, codeword_count = 0, h, j, M, rows, pad_count = 0;
int x_count, y_count, z_count, posn_val, local_value;
String intermediate = "";
String localpattern = "";
final int[] codewords = new int[170];
final int[][] c_grid = new int[8][8];
final int[][] w_grid = new int[8][4];
 
if (!this.content.matches("[\u0000-\u007F]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
this.inputData = toBytes(this.content, StandardCharsets.US_ASCII);
 
if (this.inputDataType == DataType.GS1) {
intermediate += "*"; // FNC1
}
 
for (i = 0; i < this.inputData.length; i++) {
final int c = this.inputData[i];
if (c == FNC1) {
intermediate += "*"; // FNC1
} else {
intermediate += C49_TABLE7[c];
}
}
 
h = intermediate.length();
 
i = 0;
do {
if (intermediate.charAt(i) >= '0' && intermediate.charAt(i) <= '9') {
 
/* Numeric data */
int latch = 0;
j = 0;
do {
if (i + j >= h) {
latch = 1;
} else {
if (intermediate.charAt(i + j) >= '0' && intermediate.charAt(i + j) <= '9') {
j++;
} else {
latch = 1;
}
}
} while (latch == 0);
if (j >= 5) {
/* Use Numeric Encodation Method */
int block_count, c;
int block_remain;
int block_value;
 
codewords[codeword_count] = 48; /* Numeric Shift */
codeword_count++;
 
block_count = j / 5;
block_remain = j % 5;
 
for (c = 0; c < block_count; c++) {
if (c == block_count - 1 && block_remain == 2) {
/* Rule (d) */
block_value = 100000;
block_value += (intermediate.charAt(i) - '0') * 1000;
block_value += (intermediate.charAt(i + 1) - '0') * 100;
block_value += (intermediate.charAt(i + 2) - '0') * 10;
block_value += intermediate.charAt(i + 3) - '0';
 
codewords[codeword_count] = block_value / (48 * 48);
block_value = block_value - 48 * 48 * codewords[codeword_count];
codeword_count++;
codewords[codeword_count] = block_value / 48;
block_value = block_value - 48 * codewords[codeword_count];
codeword_count++;
codewords[codeword_count] = block_value;
codeword_count++;
i += 4;
block_value = (intermediate.charAt(i) - '0') * 100;
block_value += (intermediate.charAt(i + 1) - '0') * 10;
block_value += intermediate.charAt(i + 2) - '0';
 
codewords[codeword_count] = block_value / 48;
block_value = block_value - 48 * codewords[codeword_count];
codeword_count++;
codewords[codeword_count] = block_value;
codeword_count++;
i += 3;
} else {
block_value = (intermediate.charAt(i) - '0') * 10000;
block_value += (intermediate.charAt(i + 1) - '0') * 1000;
block_value += (intermediate.charAt(i + 2) - '0') * 100;
block_value += (intermediate.charAt(i + 3) - '0') * 10;
block_value += intermediate.charAt(i + 4) - '0';
 
codewords[codeword_count] = block_value / (48 * 48);
block_value = block_value - 48 * 48 * codewords[codeword_count];
codeword_count++;
codewords[codeword_count] = block_value / 48;
block_value = block_value - 48 * codewords[codeword_count];
codeword_count++;
codewords[codeword_count] = block_value;
codeword_count++;
i += 5;
}
}
 
switch (block_remain) {
case 1:
/* Rule (a) */
codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_SET);
codeword_count++;
i++;
break;
case 3:
/* Rule (b) */
block_value = (intermediate.charAt(i) - '0') * 100;
block_value += (intermediate.charAt(i + 1) - '0') * 10;
block_value += intermediate.charAt(i + 2) - '0';
 
codewords[codeword_count] = block_value / 48;
block_value = block_value - 48 * codewords[codeword_count];
codeword_count++;
codewords[codeword_count] = block_value;
codeword_count++;
i += 3;
break;
case 4:
/* Rule (c) */
block_value = 100000;
block_value += (intermediate.charAt(i) - '0') * 1000;
block_value += (intermediate.charAt(i + 1) - '0') * 100;
block_value += (intermediate.charAt(i + 2) - '0') * 10;
block_value += intermediate.charAt(i + 3) - '0';
 
codewords[codeword_count] = block_value / (48 * 48);
block_value = block_value - 48 * 48 * codewords[codeword_count];
codeword_count++;
codewords[codeword_count] = block_value / 48;
block_value = block_value - 48 * codewords[codeword_count];
codeword_count++;
codewords[codeword_count] = block_value;
codeword_count++;
i += 4;
break;
}
if (i < h) {
/* There is more to add */
codewords[codeword_count] = 48; /* Numeric Shift */
codeword_count++;
}
} else {
codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_SET);
codeword_count++;
i++;
}
} else {
codewords[codeword_count] = positionOf(intermediate.charAt(i), C49_SET);
codeword_count++;
i++;
}
} while (i < h);
 
switch (codewords[0]) { /* Set starting mode value */
case 48:
M = 2;
break;
case 43:
M = 4;
break;
case 44:
M = 5;
break;
default:
M = 0;
break;
}
 
if (M != 0) {
for (i = 0; i < codeword_count; i++) {
codewords[i] = codewords[i + 1];
}
codeword_count--;
}
 
if (codeword_count > 49) {
throw new OkapiException("Input too long");
}
 
infoLine("Starting Mode (M): " + M);
 
/* Place codewords in code character array (c grid) */
rows = 0;
do {
for (i = 0; i < 7; i++) {
if (rows * 7 + i < codeword_count) {
c_grid[rows][i] = codewords[rows * 7 + i];
} else {
c_grid[rows][i] = 48; /* Pad */
pad_count++;
}
}
rows++;
} while (rows * 7 < codeword_count);
 
if (rows <= 6 && pad_count < 5 || rows > 6 || rows == 1) {
/* Add a row */
for (i = 0; i < 7; i++) {
c_grid[rows][i] = 48; /* Pad */
}
rows++;
}
 
/* Add row count and mode character */
c_grid[rows - 1][6] = 7 * (rows - 2) + M;
 
/* Add row check character */
for (i = 0; i < rows - 1; i++) {
int row_sum = 0;
 
for (j = 0; j < 7; j++) {
row_sum += c_grid[i][j];
}
c_grid[i][7] = row_sum % 49;
}
 
/* Calculate Symbol Check Characters */
posn_val = 0;
x_count = c_grid[rows - 1][6] * 20;
y_count = c_grid[rows - 1][6] * 16;
z_count = c_grid[rows - 1][6] * 38;
for (i = 0; i < rows - 1; i++) {
for (j = 0; j < 4; j++) {
local_value = c_grid[i][2 * j] * 49 + c_grid[i][2 * j + 1];
x_count += C49_X_WEIGHT[posn_val] * local_value;
y_count += C49_Y_WEIGHT[posn_val] * local_value;
z_count += C49_Z_WEIGHT[posn_val] * local_value;
posn_val++;
}
}
 
if (rows > 6) {
/* Add Z Symbol Check */
c_grid[rows - 1][0] = z_count % 2401 / 49;
c_grid[rows - 1][1] = z_count % 2401 % 49;
}
 
local_value = c_grid[rows - 1][0] * 49 + c_grid[rows - 1][1];
x_count += C49_X_WEIGHT[posn_val] * local_value;
y_count += C49_Y_WEIGHT[posn_val] * local_value;
posn_val++;
 
/* Add Y Symbol Check */
c_grid[rows - 1][2] = y_count % 2401 / 49;
c_grid[rows - 1][3] = y_count % 2401 % 49;
 
local_value = c_grid[rows - 1][2] * 49 + c_grid[rows - 1][3];
x_count += C49_X_WEIGHT[posn_val] * local_value;
 
/* Add X Symbol Check */
c_grid[rows - 1][4] = x_count % 2401 / 49;
c_grid[rows - 1][5] = x_count % 2401 % 49;
 
infoLine("Check Characters: " + z_count % 2401 + " " + y_count % 2401);
 
/* Add last row check character */
j = 0;
for (i = 0; i < 7; i++) {
j += c_grid[rows - 1][i];
}
c_grid[rows - 1][7] = j % 49;
 
info("Codewords: ");
/* Transfer data to symbol character array (w grid) */
for (i = 0; i < rows; i++) {
for (j = 0; j < 4; j++) {
w_grid[i][j] = c_grid[i][2 * j] * 49 + c_grid[i][2 * j + 1];
infoSpace(c_grid[i][2 * j]);
infoSpace(c_grid[i][2 * j + 1]);
}
}
infoLine();
 
this.readable = "";
this.pattern = new String[rows];
this.row_count = rows;
this.row_height = new int[rows];
 
info("Symbol Characters: ");
for (i = 0; i < rows; i++) {
localpattern = "11"; /* Start character */
for (j = 0; j < 4; j++) {
infoSpace(w_grid[i][j]);
if (i != rows - 1) {
if (C49_TABLE4[i].charAt(j) == 'E') {
/* Even Parity */
localpattern += C49_APPXE_EVEN[w_grid[i][j]];
} else {
/* Odd Parity */
localpattern += C49_APPXE_ODD[w_grid[i][j]];
}
} else {
/* Last row uses all even parity */
localpattern += C49_APPXE_EVEN[w_grid[i][j]];
}
}
localpattern += "4"; /* Stop character */
 
this.pattern[i] = localpattern;
this.row_height[i] = 10;
 
}
infoLine();
}
 
@Override
protected void plotSymbol() {
 
int xBlock, yBlock;
int x, y, w, h;
boolean black;
 
this.rectangles.clear();
y = 1;
h = 1;
for (yBlock = 0; yBlock < this.row_count; yBlock++) {
black = true;
x = 15;
for (xBlock = 0; xBlock < this.pattern[yBlock].length(); xBlock++) {
if (black) {
black = false;
w = this.pattern[yBlock].charAt(xBlock) - '0';
if (this.row_height[yBlock] == -1) {
h = this.default_height;
} else {
h = this.row_height[yBlock];
}
if (w != 0 && h != 0) {
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
this.rectangles.add(rect);
}
if (x + w > this.symbol_width) {
this.symbol_width = x + w;
}
} else {
black = true;
}
x += this.pattern[yBlock].charAt(xBlock) - '0';
}
y += h;
if (y > this.symbol_height) {
this.symbol_height = y;
}
/* Add bars between rows */
if (yBlock != this.row_count - 1) {
final Rectangle2D.Double rect = new Rectangle2D.Double(15, y - 1, this.symbol_width - 15, 2);
this.rectangles.add(rect);
}
}
 
/* Add top and bottom binding bars */
final Rectangle2D.Double top = new Rectangle2D.Double(0, 0, this.symbol_width + 15, 2);
this.rectangles.add(top);
final Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, this.symbol_width + 15, 2);
this.rectangles.add(bottom);
this.symbol_width += 15;
this.symbol_height += 1;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Upc.java
New file
0,0 → 1,411
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.backend.Ean.calcDigit;
import static uk.org.okapibarcode.backend.Ean.validateAndPad;
import static uk.org.okapibarcode.backend.HumanReadableLocation.BOTTOM;
import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE;
import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP;
 
import java.awt.geom.Rectangle2D;
import java.util.Arrays;
 
/**
* <p>
* Implements UPC bar code symbology according to BS EN 797:1996.
*
* <p>
* UPC-A requires an 11 digit article number. The check digit is calculated. UPC-E is a
* zero-compressed version of UPC-A developed for smaller packages. The code requires a 6 digit
* article number (digits 0-9). The check digit is calculated. Also supports Number System 1
* encoding by entering a 7-digit article number stating with the digit 1.
*
* <p>
* EAN-2 and EAN-5 add-on symbols can be added using the '+' character followed by the add-on data.
*
* @author <a href="mailto:jakel2006@me.com">Robert Elliott</a>
*/
public class Upc extends Symbol {
 
public static enum Mode {
UPCA, UPCE
};
 
private static final String[] SET_AC = { "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213", "3112" };
 
private static final String[] SET_B = { "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121", "2113" };
 
/* Number set for UPC-E symbol (EN Table 4) */
private static final String[] UPC_PARITY_0 = { "BBBAAA", "BBABAA", "BBAABA", "BBAAAB", "BABBAA", "BAABBA", "BAAABB", "BABABA", "BABAAB", "BAABAB" };
 
/* Not covered by BS EN 797 */
private static final String[] UPC_PARITY_1 = { "AAABBB", "AABABB", "AABBAB", "AABBBA", "ABAABB", "ABBAAB", "ABBBAA", "ABABAB", "ABABBA", "ABBABA" };
 
private Mode mode = Mode.UPCA;
private boolean showCheckDigit = true;
private int guardPatternExtraHeight = 5;
private boolean linkageFlag;
private EanUpcAddOn addOn;
 
/** Creates a new instance. */
public Upc() {
this.humanReadableAlignment = HumanReadableAlignment.JUSTIFY;
}
 
/**
* Sets the UPC mode (UPC-A or UPC-E). The default is UPC-A.
*
* @param mode the UPC mode (UPC-A or UPC-E)
*/
public void setMode(final Mode mode) {
this.mode = mode;
}
 
/**
* Returns the UPC mode (UPC-A or UPC-E).
*
* @return the UPC mode (UPC-A or UPC-E)
*/
public Mode getMode() {
return this.mode;
}
 
/**
* Sets whether or not to show the check digit in the human-readable text.
*
* @param showCheckDigit whether or not to show the check digit in the human-readable text
*/
public void setShowCheckDigit(final boolean showCheckDigit) {
this.showCheckDigit = showCheckDigit;
}
 
/**
* Returns whether or not to show the check digit in the human-readable text.
*
* @return whether or not to show the check digit in the human-readable text
*/
public boolean getShowCheckDigit() {
return this.showCheckDigit;
}
 
/**
* Sets the extra height used for the guard patterns. The default value is <code>5</code>.
*
* @param guardPatternExtraHeight the extra height used for the guard patterns
*/
public void setGuardPatternExtraHeight(final int guardPatternExtraHeight) {
this.guardPatternExtraHeight = guardPatternExtraHeight;
}
 
/**
* Returns the extra height used for the guard patterns.
*
* @return the extra height used for the guard patterns
*/
public int getGuardPatternExtraHeight() {
return this.guardPatternExtraHeight;
}
 
/**
* Sets the linkage flag. If set to <code>true</code>, this symbol is part of a composite
* symbol.
*
* @param linkageFlag the linkage flag
*/
protected void setLinkageFlag(final boolean linkageFlag) {
this.linkageFlag = linkageFlag;
}
 
@Override
protected void encode() {
 
separateContent();
 
if (this.content.isEmpty()) {
throw new OkapiException("Missing UPC data");
}
 
if (this.mode == Mode.UPCA) {
upca();
} else {
upce();
}
}
 
private void separateContent() {
final int splitPoint = this.content.indexOf('+');
if (splitPoint == -1) {
// there is no add-on data
this.addOn = null;
} else if (splitPoint == this.content.length() - 1) {
// we found the add-on separator, but no add-on data
throw new OkapiException("Invalid add-on data");
} else {
// there is a '+' in the input data, use an add-on EAN2 or EAN5
this.addOn = new EanUpcAddOn();
this.addOn.font = this.font;
this.addOn.fontName = this.fontName;
this.addOn.fontSize = this.fontSize;
this.addOn.humanReadableLocation = this.humanReadableLocation == NONE ? NONE : TOP;
this.addOn.moduleWidth = this.moduleWidth;
this.addOn.default_height = this.default_height + this.guardPatternExtraHeight - 8;
this.addOn.setContent(this.content.substring(splitPoint + 1));
this.content = this.content.substring(0, splitPoint);
}
}
 
private void upca() {
 
this.content = validateAndPad(this.content, 11);
 
final char check = calcDigit(this.content);
infoLine("Check Digit: " + check);
 
final String hrt = this.content + check;
 
final StringBuilder dest = new StringBuilder("111");
for (int i = 0; i < 12; i++) {
if (i == 6) {
dest.append("11111");
}
dest.append(SET_AC[hrt.charAt(i) - '0']);
}
dest.append("111");
 
this.readable = hrt;
this.pattern = new String[] { dest.toString() };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private void upce() {
 
this.content = validateAndPad(this.content, 7);
 
final String expanded = expandToEquivalentUpcA(this.content, true);
infoLine("UPC-A Equivalent: " + expanded);
 
final char check = calcDigit(expanded);
infoLine("Check Digit: " + check);
 
final String hrt = this.content + check;
 
final int numberSystem = getNumberSystem(this.content);
final String[] parityArray = numberSystem == 1 ? UPC_PARITY_1 : UPC_PARITY_0;
final String parity = parityArray[check - '0'];
 
final StringBuilder dest = new StringBuilder("111");
for (int i = 0; i < 6; i++) {
if (parity.charAt(i) == 'A') {
dest.append(SET_AC[this.content.charAt(i + 1) - '0']);
} else { // B
dest.append(SET_B[this.content.charAt(i + 1) - '0']);
}
}
dest.append("111111");
 
this.readable = hrt;
this.pattern = new String[] { dest.toString() };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
/**
* Expands the zero-compressed UPC-E code to make a UPC-A equivalent (EN Table 5).
*
* @param content the UPC-E code to expand
* @param validate whether or not to validate the input
* @return the UPC-A equivalent of the specified UPC-E code
*/
protected String expandToEquivalentUpcA(final String content, final boolean validate) {
 
final char[] upce = content.toCharArray();
final char[] upca = new char[11];
Arrays.fill(upca, '0');
upca[0] = upce[0];
upca[1] = upce[1];
upca[2] = upce[2];
 
final char emode = upce[6];
 
switch (emode) {
case '0':
case '1':
case '2':
upca[3] = emode;
upca[8] = upce[3];
upca[9] = upce[4];
upca[10] = upce[5];
break;
case '3':
upca[3] = upce[3];
upca[9] = upce[4];
upca[10] = upce[5];
if (validate && (upce[3] == '0' || upce[3] == '1' || upce[3] == '2')) {
/* Note 1 - "X3 shall not be equal to 0, 1 or 2" */
throw new OkapiException("Invalid UPC-E data");
}
break;
case '4':
upca[3] = upce[3];
upca[4] = upce[4];
upca[10] = upce[5];
if (validate && upce[4] == '0') {
/* Note 2 - "X4 shall not be equal to 0" */
throw new OkapiException("Invalid UPC-E data");
}
break;
default:
upca[3] = upce[3];
upca[4] = upce[4];
upca[5] = upce[5];
upca[10] = emode;
if (validate && upce[5] == '0') {
/* Note 3 - "X5 shall not be equal to 0" */
throw new OkapiException("Invalid UPC-E data");
}
break;
}
 
return new String(upca);
}
 
/** Two number systems can be used: system 0 and system 1. */
private static int getNumberSystem(final String content) {
switch (content.charAt(0)) {
case '0':
return 0;
case '1':
return 1;
default:
throw new OkapiException("Invalid input data");
}
}
 
@Override
protected void plotSymbol() {
 
int xBlock;
int x, y, w, h;
boolean black = true;
final int compositeOffset = this.linkageFlag ? 6 : 0; // space for composite separator above
final int hrtOffset = this.humanReadableLocation == TOP ? getTheoreticalHumanReadableHeight() : 0; // space
// for
// HRT
// above
 
this.rectangles.clear();
this.texts.clear();
x = 0;
 
/* Draw the bars in the symbology */
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
 
w = this.pattern[0].charAt(xBlock) - '0';
 
if (black) {
y = 0;
h = this.default_height;
/* Add extension to guide bars */
if (this.mode == Mode.UPCA) {
if (x < 10 || x > 84 || x > 45 && x < 49) {
h += this.guardPatternExtraHeight;
}
if (this.linkageFlag && (x == 0 || x == 94)) {
h += 2;
y -= 2;
}
} else {
if (x < 4 || x > 45) {
h += this.guardPatternExtraHeight;
}
if (this.linkageFlag && (x == 0 || x == 50)) {
h += 2;
y -= 2;
}
}
final Rectangle2D.Double rect = new Rectangle2D.Double(scale(x), y + compositeOffset + hrtOffset, scale(w), h);
this.rectangles.add(rect);
this.symbol_width = Math.max(this.symbol_width, (int) rect.getMaxX());
this.symbol_height = Math.max(this.symbol_height, (int) rect.getHeight());
}
 
black = !black;
x += w;
}
 
/* Add separator for composite symbology, if necessary */
if (this.linkageFlag) {
if (this.mode == Mode.UPCA) {
this.rectangles.add(new Rectangle2D.Double(scale(0), 0, scale(1), 2));
this.rectangles.add(new Rectangle2D.Double(scale(94), 0, scale(1), 2));
this.rectangles.add(new Rectangle2D.Double(scale(-1), 2, scale(1), 2));
this.rectangles.add(new Rectangle2D.Double(scale(95), 2, scale(1), 2));
} else { // UPCE
this.rectangles.add(new Rectangle2D.Double(scale(0), 0, scale(1), 2));
this.rectangles.add(new Rectangle2D.Double(scale(50), 0, scale(1), 2));
this.rectangles.add(new Rectangle2D.Double(scale(-1), 2, scale(1), 2));
this.rectangles.add(new Rectangle2D.Double(scale(51), 2, scale(1), 2));
}
this.symbol_height += 4;
}
 
/* Now add the text */
if (this.humanReadableLocation == BOTTOM) {
this.symbol_height -= this.guardPatternExtraHeight;
final double baseline = this.symbol_height + this.fontSize;
if (this.mode == Mode.UPCA) {
this.texts.add(new TextBox(scale(-9), baseline, scale(4), this.readable.substring(0, 1), HumanReadableAlignment.RIGHT));
this.texts.add(new TextBox(scale(12), baseline, scale(32), this.readable.substring(1, 6), this.humanReadableAlignment));
this.texts.add(new TextBox(scale(51), baseline, scale(32), this.readable.substring(6, 11), this.humanReadableAlignment));
if (this.showCheckDigit) {
this.texts.add(new TextBox(scale(97), baseline, scale(4), this.readable.substring(11, 12), HumanReadableAlignment.LEFT));
}
} else { // UPCE
this.texts.add(new TextBox(scale(-9), baseline, scale(4), this.readable.substring(0, 1), HumanReadableAlignment.RIGHT));
this.texts.add(new TextBox(scale(5), baseline, scale(39), this.readable.substring(1, 7), this.humanReadableAlignment));
if (this.showCheckDigit) {
this.texts.add(new TextBox(scale(53), baseline, scale(4), this.readable.substring(7, 8), HumanReadableAlignment.LEFT));
}
}
} else if (this.humanReadableLocation == TOP) {
final double baseline = this.fontSize;
final int width = this.mode == Mode.UPCA ? 94 : 50;
this.texts.add(new TextBox(scale(0), baseline, scale(width), this.readable, this.humanReadableAlignment));
}
 
/* Now add the add-on symbol, if necessary */
if (this.addOn != null) {
final int gap = 9;
final int baseX = this.symbol_width + scale(gap);
final Rectangle2D.Double r1 = this.rectangles.get(0);
final Rectangle2D.Double ar1 = this.addOn.rectangles.get(0);
final int baseY = (int) (r1.y + r1.getHeight() - ar1.y - ar1.getHeight());
for (final TextBox t : this.addOn.getTexts()) {
this.texts.add(new TextBox(baseX + t.x, baseY + t.y, t.width, t.text, t.alignment));
}
for (final Rectangle2D.Double r : this.addOn.getRectangles()) {
this.rectangles.add(new Rectangle2D.Double(baseX + r.x, baseY + r.y, r.width, r.height));
}
this.symbol_width += scale(gap) + this.addOn.symbol_width;
this.pattern[0] = this.pattern[0] + gap + this.addOn.pattern[0];
}
}
 
/** Scales the specified width or x-dimension according to the current module width. */
private int scale(final int w) {
return this.moduleWidth * w;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/UspsOneCode.java
New file
0,0 → 1,458
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE;
import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP;
 
import java.awt.geom.Rectangle2D;
import java.math.BigInteger;
 
/**
* <p>
* Implements USPS OneCode (also known as Intelligent Mail Barcode) according to USPS-B-3200F.
*
* <p>
* OneCode is a fixed length (65-bar) symbol which combines routing and customer information in a
* single symbol. Input data consists of a 20 digit tracking code, followed by a dash (-), followed
* by a delivery point ZIP code which can be 0, 5, 9 or 11 digits in length.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @see <a href=
* "https://ribbs.usps.gov/intelligentmail_mailpieces/documents/tech_guides/SPUSPSG.pdf">USPS
* OneCode Specification</a>
*/
public class UspsOneCode extends Symbol {
 
/* The following lookup tables were generated using the code in Appendix C */
 
/** Appendix D Table 1 - 5 of 13 characters */
private static final int[] APPX_D_I = { 0x001F, 0x1F00, 0x002F, 0x1E80, 0x0037, 0x1D80, 0x003B, 0x1B80, 0x003D, 0x1780, 0x003E, 0x0F80, 0x004F, 0x1E40, 0x0057, 0x1D40, 0x005B, 0x1B40, 0x005D,
0x1740, 0x005E, 0x0F40, 0x0067, 0x1CC0, 0x006B, 0x1AC0, 0x006D, 0x16C0, 0x006E, 0x0EC0, 0x0073, 0x19C0, 0x0075, 0x15C0, 0x0076, 0x0DC0, 0x0079, 0x13C0, 0x007A, 0x0BC0, 0x007C, 0x07C0,
0x008F, 0x1E20, 0x0097, 0x1D20, 0x009B, 0x1B20, 0x009D, 0x1720, 0x009E, 0x0F20, 0x00A7, 0x1CA0, 0x00AB, 0x1AA0, 0x00AD, 0x16A0, 0x00AE, 0x0EA0, 0x00B3, 0x19A0, 0x00B5, 0x15A0, 0x00B6,
0x0DA0, 0x00B9, 0x13A0, 0x00BA, 0x0BA0, 0x00BC, 0x07A0, 0x00C7, 0x1C60, 0x00CB, 0x1A60, 0x00CD, 0x1660, 0x00CE, 0x0E60, 0x00D3, 0x1960, 0x00D5, 0x1560, 0x00D6, 0x0D60, 0x00D9, 0x1360,
0x00DA, 0x0B60, 0x00DC, 0x0760, 0x00E3, 0x18E0, 0x00E5, 0x14E0, 0x00E6, 0x0CE0, 0x00E9, 0x12E0, 0x00EA, 0x0AE0, 0x00EC, 0x06E0, 0x00F1, 0x11E0, 0x00F2, 0x09E0, 0x00F4, 0x05E0, 0x00F8,
0x03E0, 0x010F, 0x1E10, 0x0117, 0x1D10, 0x011B, 0x1B10, 0x011D, 0x1710, 0x011E, 0x0F10, 0x0127, 0x1C90, 0x012B, 0x1A90, 0x012D, 0x1690, 0x012E, 0x0E90, 0x0133, 0x1990, 0x0135, 0x1590,
0x0136, 0x0D90, 0x0139, 0x1390, 0x013A, 0x0B90, 0x013C, 0x0790, 0x0147, 0x1C50, 0x014B, 0x1A50, 0x014D, 0x1650, 0x014E, 0x0E50, 0x0153, 0x1950, 0x0155, 0x1550, 0x0156, 0x0D50, 0x0159,
0x1350, 0x015A, 0x0B50, 0x015C, 0x0750, 0x0163, 0x18D0, 0x0165, 0x14D0, 0x0166, 0x0CD0, 0x0169, 0x12D0, 0x016A, 0x0AD0, 0x016C, 0x06D0, 0x0171, 0x11D0, 0x0172, 0x09D0, 0x0174, 0x05D0,
0x0178, 0x03D0, 0x0187, 0x1C30, 0x018B, 0x1A30, 0x018D, 0x1630, 0x018E, 0x0E30, 0x0193, 0x1930, 0x0195, 0x1530, 0x0196, 0x0D30, 0x0199, 0x1330, 0x019A, 0x0B30, 0x019C, 0x0730, 0x01A3,
0x18B0, 0x01A5, 0x14B0, 0x01A6, 0x0CB0, 0x01A9, 0x12B0, 0x01AA, 0x0AB0, 0x01AC, 0x06B0, 0x01B1, 0x11B0, 0x01B2, 0x09B0, 0x01B4, 0x05B0, 0x01B8, 0x03B0, 0x01C3, 0x1870, 0x01C5, 0x1470,
0x01C6, 0x0C70, 0x01C9, 0x1270, 0x01CA, 0x0A70, 0x01CC, 0x0670, 0x01D1, 0x1170, 0x01D2, 0x0970, 0x01D4, 0x0570, 0x01D8, 0x0370, 0x01E1, 0x10F0, 0x01E2, 0x08F0, 0x01E4, 0x04F0, 0x01E8,
0x02F0, 0x020F, 0x1E08, 0x0217, 0x1D08, 0x021B, 0x1B08, 0x021D, 0x1708, 0x021E, 0x0F08, 0x0227, 0x1C88, 0x022B, 0x1A88, 0x022D, 0x1688, 0x022E, 0x0E88, 0x0233, 0x1988, 0x0235, 0x1588,
0x0236, 0x0D88, 0x0239, 0x1388, 0x023A, 0x0B88, 0x023C, 0x0788, 0x0247, 0x1C48, 0x024B, 0x1A48, 0x024D, 0x1648, 0x024E, 0x0E48, 0x0253, 0x1948, 0x0255, 0x1548, 0x0256, 0x0D48, 0x0259,
0x1348, 0x025A, 0x0B48, 0x025C, 0x0748, 0x0263, 0x18C8, 0x0265, 0x14C8, 0x0266, 0x0CC8, 0x0269, 0x12C8, 0x026A, 0x0AC8, 0x026C, 0x06C8, 0x0271, 0x11C8, 0x0272, 0x09C8, 0x0274, 0x05C8,
0x0278, 0x03C8, 0x0287, 0x1C28, 0x028B, 0x1A28, 0x028D, 0x1628, 0x028E, 0x0E28, 0x0293, 0x1928, 0x0295, 0x1528, 0x0296, 0x0D28, 0x0299, 0x1328, 0x029A, 0x0B28, 0x029C, 0x0728, 0x02A3,
0x18A8, 0x02A5, 0x14A8, 0x02A6, 0x0CA8, 0x02A9, 0x12A8, 0x02AA, 0x0AA8, 0x02AC, 0x06A8, 0x02B1, 0x11A8, 0x02B2, 0x09A8, 0x02B4, 0x05A8, 0x02B8, 0x03A8, 0x02C3, 0x1868, 0x02C5, 0x1468,
0x02C6, 0x0C68, 0x02C9, 0x1268, 0x02CA, 0x0A68, 0x02CC, 0x0668, 0x02D1, 0x1168, 0x02D2, 0x0968, 0x02D4, 0x0568, 0x02D8, 0x0368, 0x02E1, 0x10E8, 0x02E2, 0x08E8, 0x02E4, 0x04E8, 0x0307,
0x1C18, 0x030B, 0x1A18, 0x030D, 0x1618, 0x030E, 0x0E18, 0x0313, 0x1918, 0x0315, 0x1518, 0x0316, 0x0D18, 0x0319, 0x1318, 0x031A, 0x0B18, 0x031C, 0x0718, 0x0323, 0x1898, 0x0325, 0x1498,
0x0326, 0x0C98, 0x0329, 0x1298, 0x032A, 0x0A98, 0x032C, 0x0698, 0x0331, 0x1198, 0x0332, 0x0998, 0x0334, 0x0598, 0x0338, 0x0398, 0x0343, 0x1858, 0x0345, 0x1458, 0x0346, 0x0C58, 0x0349,
0x1258, 0x034A, 0x0A58, 0x034C, 0x0658, 0x0351, 0x1158, 0x0352, 0x0958, 0x0354, 0x0558, 0x0361, 0x10D8, 0x0362, 0x08D8, 0x0364, 0x04D8, 0x0383, 0x1838, 0x0385, 0x1438, 0x0386, 0x0C38,
0x0389, 0x1238, 0x038A, 0x0A38, 0x038C, 0x0638, 0x0391, 0x1138, 0x0392, 0x0938, 0x0394, 0x0538, 0x03A1, 0x10B8, 0x03A2, 0x08B8, 0x03A4, 0x04B8, 0x03C1, 0x1078, 0x03C2, 0x0878, 0x03C4,
0x0478, 0x040F, 0x1E04, 0x0417, 0x1D04, 0x041B, 0x1B04, 0x041D, 0x1704, 0x041E, 0x0F04, 0x0427, 0x1C84, 0x042B, 0x1A84, 0x042D, 0x1684, 0x042E, 0x0E84, 0x0433, 0x1984, 0x0435, 0x1584,
0x0436, 0x0D84, 0x0439, 0x1384, 0x043A, 0x0B84, 0x043C, 0x0784, 0x0447, 0x1C44, 0x044B, 0x1A44, 0x044D, 0x1644, 0x044E, 0x0E44, 0x0453, 0x1944, 0x0455, 0x1544, 0x0456, 0x0D44, 0x0459,
0x1344, 0x045A, 0x0B44, 0x045C, 0x0744, 0x0463, 0x18C4, 0x0465, 0x14C4, 0x0466, 0x0CC4, 0x0469, 0x12C4, 0x046A, 0x0AC4, 0x046C, 0x06C4, 0x0471, 0x11C4, 0x0472, 0x09C4, 0x0474, 0x05C4,
0x0487, 0x1C24, 0x048B, 0x1A24, 0x048D, 0x1624, 0x048E, 0x0E24, 0x0493, 0x1924, 0x0495, 0x1524, 0x0496, 0x0D24, 0x0499, 0x1324, 0x049A, 0x0B24, 0x049C, 0x0724, 0x04A3, 0x18A4, 0x04A5,
0x14A4, 0x04A6, 0x0CA4, 0x04A9, 0x12A4, 0x04AA, 0x0AA4, 0x04AC, 0x06A4, 0x04B1, 0x11A4, 0x04B2, 0x09A4, 0x04B4, 0x05A4, 0x04C3, 0x1864, 0x04C5, 0x1464, 0x04C6, 0x0C64, 0x04C9, 0x1264,
0x04CA, 0x0A64, 0x04CC, 0x0664, 0x04D1, 0x1164, 0x04D2, 0x0964, 0x04D4, 0x0564, 0x04E1, 0x10E4, 0x04E2, 0x08E4, 0x0507, 0x1C14, 0x050B, 0x1A14, 0x050D, 0x1614, 0x050E, 0x0E14, 0x0513,
0x1914, 0x0515, 0x1514, 0x0516, 0x0D14, 0x0519, 0x1314, 0x051A, 0x0B14, 0x051C, 0x0714, 0x0523, 0x1894, 0x0525, 0x1494, 0x0526, 0x0C94, 0x0529, 0x1294, 0x052A, 0x0A94, 0x052C, 0x0694,
0x0531, 0x1194, 0x0532, 0x0994, 0x0534, 0x0594, 0x0543, 0x1854, 0x0545, 0x1454, 0x0546, 0x0C54, 0x0549, 0x1254, 0x054A, 0x0A54, 0x054C, 0x0654, 0x0551, 0x1154, 0x0552, 0x0954, 0x0561,
0x10D4, 0x0562, 0x08D4, 0x0583, 0x1834, 0x0585, 0x1434, 0x0586, 0x0C34, 0x0589, 0x1234, 0x058A, 0x0A34, 0x058C, 0x0634, 0x0591, 0x1134, 0x0592, 0x0934, 0x05A1, 0x10B4, 0x05A2, 0x08B4,
0x05C1, 0x1074, 0x05C2, 0x0874, 0x0607, 0x1C0C, 0x060B, 0x1A0C, 0x060D, 0x160C, 0x060E, 0x0E0C, 0x0613, 0x190C, 0x0615, 0x150C, 0x0616, 0x0D0C, 0x0619, 0x130C, 0x061A, 0x0B0C, 0x061C,
0x070C, 0x0623, 0x188C, 0x0625, 0x148C, 0x0626, 0x0C8C, 0x0629, 0x128C, 0x062A, 0x0A8C, 0x062C, 0x068C, 0x0631, 0x118C, 0x0632, 0x098C, 0x0643, 0x184C, 0x0645, 0x144C, 0x0646, 0x0C4C,
0x0649, 0x124C, 0x064A, 0x0A4C, 0x0651, 0x114C, 0x0652, 0x094C, 0x0661, 0x10CC, 0x0662, 0x08CC, 0x0683, 0x182C, 0x0685, 0x142C, 0x0686, 0x0C2C, 0x0689, 0x122C, 0x068A, 0x0A2C, 0x0691,
0x112C, 0x0692, 0x092C, 0x06A1, 0x10AC, 0x06A2, 0x08AC, 0x06C1, 0x106C, 0x06C2, 0x086C, 0x0703, 0x181C, 0x0705, 0x141C, 0x0706, 0x0C1C, 0x0709, 0x121C, 0x070A, 0x0A1C, 0x0711, 0x111C,
0x0712, 0x091C, 0x0721, 0x109C, 0x0722, 0x089C, 0x0741, 0x105C, 0x0742, 0x085C, 0x0781, 0x103C, 0x0782, 0x083C, 0x080F, 0x1E02, 0x0817, 0x1D02, 0x081B, 0x1B02, 0x081D, 0x1702, 0x081E,
0x0F02, 0x0827, 0x1C82, 0x082B, 0x1A82, 0x082D, 0x1682, 0x082E, 0x0E82, 0x0833, 0x1982, 0x0835, 0x1582, 0x0836, 0x0D82, 0x0839, 0x1382, 0x083A, 0x0B82, 0x0847, 0x1C42, 0x084B, 0x1A42,
0x084D, 0x1642, 0x084E, 0x0E42, 0x0853, 0x1942, 0x0855, 0x1542, 0x0856, 0x0D42, 0x0859, 0x1342, 0x085A, 0x0B42, 0x0863, 0x18C2, 0x0865, 0x14C2, 0x0866, 0x0CC2, 0x0869, 0x12C2, 0x086A,
0x0AC2, 0x0871, 0x11C2, 0x0872, 0x09C2, 0x0887, 0x1C22, 0x088B, 0x1A22, 0x088D, 0x1622, 0x088E, 0x0E22, 0x0893, 0x1922, 0x0895, 0x1522, 0x0896, 0x0D22, 0x0899, 0x1322, 0x089A, 0x0B22,
0x08A3, 0x18A2, 0x08A5, 0x14A2, 0x08A6, 0x0CA2, 0x08A9, 0x12A2, 0x08AA, 0x0AA2, 0x08B1, 0x11A2, 0x08B2, 0x09A2, 0x08C3, 0x1862, 0x08C5, 0x1462, 0x08C6, 0x0C62, 0x08C9, 0x1262, 0x08CA,
0x0A62, 0x08D1, 0x1162, 0x08D2, 0x0962, 0x08E1, 0x10E2, 0x0907, 0x1C12, 0x090B, 0x1A12, 0x090D, 0x1612, 0x090E, 0x0E12, 0x0913, 0x1912, 0x0915, 0x1512, 0x0916, 0x0D12, 0x0919, 0x1312,
0x091A, 0x0B12, 0x0923, 0x1892, 0x0925, 0x1492, 0x0926, 0x0C92, 0x0929, 0x1292, 0x092A, 0x0A92, 0x0931, 0x1192, 0x0932, 0x0992, 0x0943, 0x1852, 0x0945, 0x1452, 0x0946, 0x0C52, 0x0949,
0x1252, 0x094A, 0x0A52, 0x0951, 0x1152, 0x0961, 0x10D2, 0x0983, 0x1832, 0x0985, 0x1432, 0x0986, 0x0C32, 0x0989, 0x1232, 0x098A, 0x0A32, 0x0991, 0x1132, 0x09A1, 0x10B2, 0x09C1, 0x1072,
0x0A07, 0x1C0A, 0x0A0B, 0x1A0A, 0x0A0D, 0x160A, 0x0A0E, 0x0E0A, 0x0A13, 0x190A, 0x0A15, 0x150A, 0x0A16, 0x0D0A, 0x0A19, 0x130A, 0x0A1A, 0x0B0A, 0x0A23, 0x188A, 0x0A25, 0x148A, 0x0A26,
0x0C8A, 0x0A29, 0x128A, 0x0A2A, 0x0A8A, 0x0A31, 0x118A, 0x0A43, 0x184A, 0x0A45, 0x144A, 0x0A46, 0x0C4A, 0x0A49, 0x124A, 0x0A51, 0x114A, 0x0A61, 0x10CA, 0x0A83, 0x182A, 0x0A85, 0x142A,
0x0A86, 0x0C2A, 0x0A89, 0x122A, 0x0A91, 0x112A, 0x0AA1, 0x10AA, 0x0AC1, 0x106A, 0x0B03, 0x181A, 0x0B05, 0x141A, 0x0B06, 0x0C1A, 0x0B09, 0x121A, 0x0B11, 0x111A, 0x0B21, 0x109A, 0x0B41,
0x105A, 0x0B81, 0x103A, 0x0C07, 0x1C06, 0x0C0B, 0x1A06, 0x0C0D, 0x1606, 0x0C0E, 0x0E06, 0x0C13, 0x1906, 0x0C15, 0x1506, 0x0C16, 0x0D06, 0x0C19, 0x1306, 0x0C23, 0x1886, 0x0C25, 0x1486,
0x0C26, 0x0C86, 0x0C29, 0x1286, 0x0C31, 0x1186, 0x0C43, 0x1846, 0x0C45, 0x1446, 0x0C49, 0x1246, 0x0C51, 0x1146, 0x0C61, 0x10C6, 0x0C83, 0x1826, 0x0C85, 0x1426, 0x0C89, 0x1226, 0x0C91,
0x1126, 0x0CA1, 0x10A6, 0x0CC1, 0x1066, 0x0D03, 0x1816, 0x0D05, 0x1416, 0x0D09, 0x1216, 0x0D11, 0x1116, 0x0D21, 0x1096, 0x0D41, 0x1056, 0x0D81, 0x1036, 0x0E03, 0x180E, 0x0E05, 0x140E,
0x0E09, 0x120E, 0x0E11, 0x110E, 0x0E21, 0x108E, 0x0E41, 0x104E, 0x0E81, 0x102E, 0x0F01, 0x101E, 0x100F, 0x1E01, 0x1017, 0x1D01, 0x101B, 0x1B01, 0x101D, 0x1701, 0x1027, 0x1C81, 0x102B,
0x1A81, 0x102D, 0x1681, 0x1033, 0x1981, 0x1035, 0x1581, 0x1039, 0x1381, 0x1047, 0x1C41, 0x104B, 0x1A41, 0x104D, 0x1641, 0x1053, 0x1941, 0x1055, 0x1541, 0x1059, 0x1341, 0x1063, 0x18C1,
0x1065, 0x14C1, 0x1069, 0x12C1, 0x1071, 0x11C1, 0x1087, 0x1C21, 0x108B, 0x1A21, 0x108D, 0x1621, 0x1093, 0x1921, 0x1095, 0x1521, 0x1099, 0x1321, 0x10A3, 0x18A1, 0x10A5, 0x14A1, 0x10A9,
0x12A1, 0x10B1, 0x11A1, 0x10C3, 0x1861, 0x10C5, 0x1461, 0x10C9, 0x1261, 0x10D1, 0x1161, 0x1107, 0x1C11, 0x110B, 0x1A11, 0x110D, 0x1611, 0x1113, 0x1911, 0x1115, 0x1511, 0x1119, 0x1311,
0x1123, 0x1891, 0x1125, 0x1491, 0x1129, 0x1291, 0x1131, 0x1191, 0x1143, 0x1851, 0x1145, 0x1451, 0x1149, 0x1251, 0x1183, 0x1831, 0x1185, 0x1431, 0x1189, 0x1231, 0x1207, 0x1C09, 0x120B,
0x1A09, 0x120D, 0x1609, 0x1213, 0x1909, 0x1215, 0x1509, 0x1219, 0x1309, 0x1223, 0x1889, 0x1225, 0x1489, 0x1229, 0x1289, 0x1243, 0x1849, 0x1245, 0x1449, 0x1283, 0x1829, 0x1285, 0x1429,
0x1303, 0x1819, 0x1305, 0x1419, 0x1407, 0x1C05, 0x140B, 0x1A05, 0x140D, 0x1605, 0x1413, 0x1905, 0x1415, 0x1505, 0x1423, 0x1885, 0x1425, 0x1485, 0x1443, 0x1845, 0x1483, 0x1825, 0x1503,
0x1815, 0x1603, 0x180D, 0x1807, 0x1C03, 0x180B, 0x1A03, 0x1813, 0x1903, 0x1823, 0x1883, 0x1843, 0x1445, 0x1249, 0x1151, 0x10E1, 0x0C46, 0x0A4A, 0x0952, 0x08E2, 0x064C, 0x0554, 0x04E4,
0x0358, 0x02E8, 0x01F0 };
 
/** Appendix D Table II - 2 of 13 characters */
private static final int[] APPX_D_II = { 0x0003, 0x1800, 0x0005, 0x1400, 0x0006, 0x0C00, 0x0009, 0x1200, 0x000A, 0x0A00, 0x000C, 0x0600, 0x0011, 0x1100, 0x0012, 0x0900, 0x0014, 0x0500, 0x0018,
0x0300, 0x0021, 0x1080, 0x0022, 0x0880, 0x0024, 0x0480, 0x0028, 0x0280, 0x0030, 0x0180, 0x0041, 0x1040, 0x0042, 0x0840, 0x0044, 0x0440, 0x0048, 0x0240, 0x0050, 0x0140, 0x0060, 0x00C0,
0x0081, 0x1020, 0x0082, 0x0820, 0x0084, 0x0420, 0x0088, 0x0220, 0x0090, 0x0120, 0x0101, 0x1010, 0x0102, 0x0810, 0x0104, 0x0410, 0x0108, 0x0210, 0x0201, 0x1008, 0x0202, 0x0808, 0x0204,
0x0408, 0x0401, 0x1004, 0x0402, 0x0804, 0x0801, 0x1002, 0x1001, 0x0802, 0x0404, 0x0208, 0x0110, 0x00A0 };
 
/** Appendix D Table IV - Bar-to-Character Mapping (reverse lookup) */
private static final int[] APPX_D_IV = { 67, 6, 78, 16, 86, 95, 34, 40, 45, 113, 117, 121, 62, 87, 18, 104, 41, 76, 57, 119, 115, 72, 97, 2, 127, 26, 105, 35, 122, 52, 114, 7, 24, 82, 68, 63, 94,
44, 77, 112, 70, 100, 39, 30, 107, 15, 125, 85, 10, 65, 54, 88, 20, 106, 46, 66, 8, 116, 29, 61, 99, 80, 90, 37, 123, 51, 25, 84, 129, 56, 4, 109, 96, 28, 36, 47, 11, 71, 33, 102, 21, 9,
17, 49, 124, 79, 64, 91, 42, 69, 53, 60, 14, 1, 27, 103, 126, 75, 89, 50, 120, 19, 32, 110, 92, 111, 130, 59, 31, 12, 81, 43, 55, 5, 74, 22, 101, 128, 58, 118, 48, 108, 38, 98, 93, 23, 83,
13, 73, 3 };
 
public UspsOneCode() {
this.default_height = 8;
this.humanReadableLocation = HumanReadableLocation.NONE;
this.humanReadableAlignment = HumanReadableAlignment.LEFT; // spec section 2.4.2
}
 
@Override
protected void encode() {
String zip = "";
String zip_adder;
String tracker = "";
int i, j;
final int length = this.content.length();
BigInteger accum;
BigInteger x_reg;
BigInteger mask;
int usps_crc;
final int[] codeword = new int[10];
final int[] characters = new int[10];
final boolean[] bar_map = new boolean[130];
char c;
 
if (!this.content.matches("[0-9\u002D]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
if (length > 32) {
throw new OkapiException("Input too long");
}
 
/* separate the tracking code from the routing code */
j = 0;
for (i = 0; i < length; i++) {
if (this.content.charAt(i) == '-') {
j = 1;
} else {
if (j == 0) {
/* reading tracker */
tracker += this.content.charAt(i);
} else {
/* reading zip code */
zip += this.content.charAt(i);
}
}
}
 
if (tracker.length() != 20) {
throw new OkapiException("Invalid length tracking code");
}
 
if (zip.length() > 11) {
throw new OkapiException("Invalid ZIP code");
}
 
/* *** Step 1 - Conversion of Data Fields into Binary Data *** */
 
/* Routing code first */
if (zip.length() > 0) {
x_reg = new BigInteger(zip);
} else {
x_reg = new BigInteger("0");
}
 
/* add weight to routing code */
if (zip.length() > 9) {
zip_adder = "1000100001";
} else {
if (zip.length() > 5) {
zip_adder = "100001";
} else {
if (zip.length() > 0) {
zip_adder = "1";
} else {
zip_adder = "0";
}
}
}
 
accum = new BigInteger(zip_adder);
accum = accum.add(x_reg);
accum = accum.multiply(BigInteger.valueOf(10));
accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(0))));
accum = accum.multiply(BigInteger.valueOf(5));
accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(1))));
for (i = 2; i < tracker.length(); i++) {
accum = accum.multiply(BigInteger.valueOf(10));
accum = accum.add(BigInteger.valueOf(Character.getNumericValue(tracker.charAt(i))));
}
 
/* *** Step 2 - Generation of 11-bit CRC on Binary Data *** */
 
final int[] byte_array = new int[13];
for (i = 0; i < byte_array.length; i++) {
mask = accum.shiftRight(96 - 8 * i);
mask = mask.and(new BigInteger("255"));
byte_array[i] = mask.intValue();
}
 
usps_crc = USPS_MSB_Math_CRC11GenerateFrameCheckSequence(byte_array);
 
/* *** Step 3 - Conversion from Binary Data to Codewords *** */
 
/* start with codeword J which is base 636 */
x_reg = accum.mod(BigInteger.valueOf(636));
codeword[9] = x_reg.intValue();
accum = accum.subtract(x_reg);
accum = accum.divide(BigInteger.valueOf(636));
 
for (i = 8; i >= 0; i--) {
x_reg = accum.mod(BigInteger.valueOf(1365));
codeword[i] = x_reg.intValue();
accum = accum.subtract(x_reg);
accum = accum.divide(BigInteger.valueOf(1365));
}
 
for (i = 0; i < 9; i++) {
if (codeword[i] == 1365) {
codeword[i] = 0;
}
}
 
/* *** Step 4 - Inserting Additional Information into Codewords *** */
 
codeword[9] = codeword[9] * 2;
 
if (usps_crc >= 1024) {
codeword[0] += 659;
}
 
info("Codewords: ");
for (i = 0; i < 10; i++) {
infoSpace(codeword[i]);
}
infoLine();
 
/* *** Step 5 - Conversion from Codewords to Characters *** */
 
for (i = 0; i < 10; i++) {
if (codeword[i] < 1287) {
characters[i] = APPX_D_I[codeword[i]];
} else {
characters[i] = APPX_D_II[codeword[i] - 1287];
}
}
 
for (i = 0; i < 10; i++) {
if ((usps_crc & 1 << i) != 0) {
characters[i] = 0x1FFF - characters[i];
}
}
 
/* *** Step 6 - Conversion from Characters to the Intelligent Mail Barcode *** */
 
for (i = 0; i < 10; i++) {
for (j = 0; j < 13; j++) {
if ((characters[i] & 1 << j) == 0) {
bar_map[APPX_D_IV[13 * i + j] - 1] = false;
} else {
bar_map[APPX_D_IV[13 * i + j] - 1] = true;
}
}
}
 
this.readable = formatHumanReadableText(this.content);
this.pattern = new String[1];
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
 
this.pattern[0] = "";
for (i = 0; i < 65; i++) {
c = 'T';
if (bar_map[i]) {
c = 'D';
}
if (bar_map[i + 65]) {
c = 'A';
}
if (bar_map[i] && bar_map[i + 65]) {
c = 'F';
}
this.pattern[0] += c;
}
 
infoLine("Encoding: " + this.pattern[0]);
}
 
private static int USPS_MSB_Math_CRC11GenerateFrameCheckSequence(final int[] bytes) {
 
final int generatorPolynomial = 0x0F35;
int frameCheckSequence = 0x07FF;
int data;
int byteIndex, bit;
int byteArrayPtr = 0;
 
/* Do most significant byte skipping the 2 most significant bits */
data = bytes[byteArrayPtr] << 5;
byteArrayPtr++;
for (bit = 2; bit < 8; bit++) {
if (((frameCheckSequence ^ data) & 0x400) != 0) {
frameCheckSequence = frameCheckSequence << 1 ^ generatorPolynomial;
} else {
frameCheckSequence = frameCheckSequence << 1;
}
frameCheckSequence &= 0x7FF;
data <<= 1;
}
 
/* Do rest of the bytes */
for (byteIndex = 1; byteIndex < 13; byteIndex++) {
data = bytes[byteArrayPtr] << 3;
byteArrayPtr++;
for (bit = 0; bit < 8; bit++) {
if (((frameCheckSequence ^ data) & 0x0400) != 0) {
frameCheckSequence = frameCheckSequence << 1 ^ generatorPolynomial;
} else {
frameCheckSequence = frameCheckSequence << 1;
}
frameCheckSequence &= 0x7FF;
data <<= 1;
}
}
 
return frameCheckSequence;
}
 
/**
* <p>
* Formats the barcode content into the correct human-readable format, per section 2.4.3 of the
* spec:
*
* <p>
* The human-readable information, when required, shall consist of the 20-digit tracking code
* and the 5-, 9-, or 11-digit routing code, if present. The fields of the tracking code, as
* defined in 2.1.3, shall be separated with a space added between data fields. When the barcode
* contains a routing code, the 5-digit ZIP Code, the 4-digit add-on, and the remaining 2 digits
* shall be separated with a space added between data fields.
*
* <p>
* Appendix F contains a good overview of the different IMb constructs / formats.
*
* @param content the content to be formatted
* @return the formatted content
*/
protected static String formatHumanReadableText(final String content) {
final StringBuilder hrt = new StringBuilder(50);
boolean mid9 = false; // 9-digit mailer ID instead of 6-digit mailer ID
boolean tracing = true; // STID indicates Origin IMb Tracing Services (050, 052)
boolean pimb = true; // barcode identifier (BI) is 94, indicating pIMb
boolean mpe5 = false; // if MPE = 5, it's a CFS/RFS variant of pIMb
int i = 0;
for (final char c : content.toCharArray()) {
if (c < '0' || c > '9') {
continue;
}
if (i == 5 && c == '9') {
mid9 = true;
}
if (i == 2 && c != '0' || i == 3 && c != '5' || i == 4 && c != '0' && c != '2') {
tracing = false;
}
if (i == 0 && c != '9' || i == 1 && c != '4') {
pimb = false;
}
if (i == 5 && c == '5') {
mpe5 = true;
}
if (i == 2 // BI -> STID
|| i == 5 // STID -> ...
|| i == 6 && pimb || i == 10 && pimb || i == 13 && pimb && !mpe5 || i == 15 && pimb && !mpe5 || i == 11 && !mid9 && !tracing && !pimb || i == 14 && mid9 && !tracing && !pimb
|| i == 20 // ... -> zip-5
|| i == 25 // zip-5 -> zip-4
|| i == 29) { // zip-4 -> zip-2
hrt.append(' ');
}
hrt.append(c);
i++;
}
return hrt.toString().trim();
}
 
@Override
protected void plotSymbol() {
int xBlock, shortHeight, longHeight;
double x, y, w, h;
 
this.rectangles.clear();
this.texts.clear();
 
int baseY;
if (this.humanReadableLocation == TOP) {
baseY = getTheoreticalHumanReadableHeight();
} else {
baseY = 0;
}
 
x = 0;
w = this.moduleWidth;
y = 0;
h = 0;
shortHeight = (int) (0.25 * this.default_height);
longHeight = (int) (0.625 * this.default_height);
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
 
switch (this.pattern[0].charAt(xBlock)) {
case 'A':
y = baseY;
h = longHeight;
break;
case 'D':
y = baseY + this.default_height - longHeight;
h = longHeight;
break;
case 'F':
y = baseY;
h = this.default_height;
break;
case 'T':
y = baseY + this.default_height - longHeight;
h = shortHeight;
break;
}
 
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
this.rectangles.add(rect);
 
x += 2.43 * w;
}
 
this.symbol_width = (int) Math.ceil((this.pattern[0].length() - 1) * 2.43 * w + w); // final
// bar
// doesn't
// need
// extra
// whitespace
this.symbol_height = this.default_height;
 
if (this.humanReadableLocation != NONE && !this.readable.isEmpty()) {
double baseline;
if (this.humanReadableLocation == TOP) {
baseline = this.fontSize;
} else {
baseline = this.symbol_height + this.fontSize;
}
this.texts.add(new TextBox(0, baseline, this.symbol_width, this.readable, this.humanReadableAlignment));
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/MaxiCode.java
New file
0,0 → 1,913
/*
* Copyright 2014-2015 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.contains;
import static uk.org.okapibarcode.util.Arrays.insertArray;
 
import java.awt.geom.Ellipse2D;
import java.util.Arrays;
 
/**
* <p>
* Implements MaxiCode according to ISO 16023:2000.
*
* <p>
* MaxiCode employs a pattern of hexagons around a central 'bulls-eye' finder pattern. Encoding in
* several modes is supported, but encoding in Mode 2 and 3 require primary messages to be set.
* Input characters can be any from the ISO 8859-1 (Latin-1) character set.
*
* <p>
* TODO: Add ECI functionality.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class MaxiCode extends Symbol {
 
/** MaxiCode module sequence, from ISO/IEC 16023 Figure 5 (30 x 33 data grid). */
private static final int[] MAXICODE_GRID = { 122, 121, 128, 127, 134, 133, 140, 139, 146, 145, 152, 151, 158, 157, 164, 163, 170, 169, 176, 175, 182, 181, 188, 187, 194, 193, 200, 199, 0, 0, 124,
123, 130, 129, 136, 135, 142, 141, 148, 147, 154, 153, 160, 159, 166, 165, 172, 171, 178, 177, 184, 183, 190, 189, 196, 195, 202, 201, 817, 0, 126, 125, 132, 131, 138, 137, 144, 143, 150,
149, 156, 155, 162, 161, 168, 167, 174, 173, 180, 179, 186, 185, 192, 191, 198, 197, 204, 203, 819, 818, 284, 283, 278, 277, 272, 271, 266, 265, 260, 259, 254, 253, 248, 247, 242, 241,
236, 235, 230, 229, 224, 223, 218, 217, 212, 211, 206, 205, 820, 0, 286, 285, 280, 279, 274, 273, 268, 267, 262, 261, 256, 255, 250, 249, 244, 243, 238, 237, 232, 231, 226, 225, 220, 219,
214, 213, 208, 207, 822, 821, 288, 287, 282, 281, 276, 275, 270, 269, 264, 263, 258, 257, 252, 251, 246, 245, 240, 239, 234, 233, 228, 227, 222, 221, 216, 215, 210, 209, 823, 0, 290, 289,
296, 295, 302, 301, 308, 307, 314, 313, 320, 319, 326, 325, 332, 331, 338, 337, 344, 343, 350, 349, 356, 355, 362, 361, 368, 367, 825, 824, 292, 291, 298, 297, 304, 303, 310, 309, 316,
315, 322, 321, 328, 327, 334, 333, 340, 339, 346, 345, 352, 351, 358, 357, 364, 363, 370, 369, 826, 0, 294, 293, 300, 299, 306, 305, 312, 311, 318, 317, 324, 323, 330, 329, 336, 335, 342,
341, 348, 347, 354, 353, 360, 359, 366, 365, 372, 371, 828, 827, 410, 409, 404, 403, 398, 397, 392, 391, 80, 79, 0, 0, 14, 13, 38, 37, 3, 0, 45, 44, 110, 109, 386, 385, 380, 379, 374, 373,
829, 0, 412, 411, 406, 405, 400, 399, 394, 393, 82, 81, 41, 0, 16, 15, 40, 39, 4, 0, 0, 46, 112, 111, 388, 387, 382, 381, 376, 375, 831, 830, 414, 413, 408, 407, 402, 401, 396, 395, 84,
83, 42, 0, 0, 0, 0, 0, 6, 5, 48, 47, 114, 113, 390, 389, 384, 383, 378, 377, 832, 0, 416, 415, 422, 421, 428, 427, 104, 103, 56, 55, 17, 0, 0, 0, 0, 0, 0, 0, 21, 20, 86, 85, 434, 433, 440,
439, 446, 445, 834, 833, 418, 417, 424, 423, 430, 429, 106, 105, 58, 57, 0, 0, 0, 0, 0, 0, 0, 0, 23, 22, 88, 87, 436, 435, 442, 441, 448, 447, 835, 0, 420, 419, 426, 425, 432, 431, 108,
107, 60, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 90, 89, 438, 437, 444, 443, 450, 449, 837, 836, 482, 481, 476, 475, 470, 469, 49, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 54, 53, 464, 463, 458,
457, 452, 451, 838, 0, 484, 483, 478, 477, 472, 471, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 466, 465, 460, 459, 454, 453, 840, 839, 486, 485, 480, 479, 474, 473, 52, 51, 32, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 43, 468, 467, 462, 461, 456, 455, 841, 0, 488, 487, 494, 493, 500, 499, 98, 97, 62, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 92, 91, 506, 505, 512, 511, 518,
517, 843, 842, 490, 489, 496, 495, 502, 501, 100, 99, 64, 63, 0, 0, 0, 0, 0, 0, 0, 0, 29, 28, 94, 93, 508, 507, 514, 513, 520, 519, 844, 0, 492, 491, 498, 497, 504, 503, 102, 101, 66, 65,
18, 0, 0, 0, 0, 0, 0, 0, 19, 30, 96, 95, 510, 509, 516, 515, 522, 521, 846, 845, 560, 559, 554, 553, 548, 547, 542, 541, 74, 73, 33, 0, 0, 0, 0, 0, 0, 11, 68, 67, 116, 115, 536, 535, 530,
529, 524, 523, 847, 0, 562, 561, 556, 555, 550, 549, 544, 543, 76, 75, 0, 0, 8, 7, 36, 35, 12, 0, 70, 69, 118, 117, 538, 537, 532, 531, 526, 525, 849, 848, 564, 563, 558, 557, 552, 551,
546, 545, 78, 77, 0, 34, 10, 9, 26, 25, 0, 0, 72, 71, 120, 119, 540, 539, 534, 533, 528, 527, 850, 0, 566, 565, 572, 571, 578, 577, 584, 583, 590, 589, 596, 595, 602, 601, 608, 607, 614,
613, 620, 619, 626, 625, 632, 631, 638, 637, 644, 643, 852, 851, 568, 567, 574, 573, 580, 579, 586, 585, 592, 591, 598, 597, 604, 603, 610, 609, 616, 615, 622, 621, 628, 627, 634, 633,
640, 639, 646, 645, 853, 0, 570, 569, 576, 575, 582, 581, 588, 587, 594, 593, 600, 599, 606, 605, 612, 611, 618, 617, 624, 623, 630, 629, 636, 635, 642, 641, 648, 647, 855, 854, 728, 727,
722, 721, 716, 715, 710, 709, 704, 703, 698, 697, 692, 691, 686, 685, 680, 679, 674, 673, 668, 667, 662, 661, 656, 655, 650, 649, 856, 0, 730, 729, 724, 723, 718, 717, 712, 711, 706, 705,
700, 699, 694, 693, 688, 687, 682, 681, 676, 675, 670, 669, 664, 663, 658, 657, 652, 651, 858, 857, 732, 731, 726, 725, 720, 719, 714, 713, 708, 707, 702, 701, 696, 695, 690, 689, 684,
683, 678, 677, 672, 671, 666, 665, 660, 659, 654, 653, 859, 0, 734, 733, 740, 739, 746, 745, 752, 751, 758, 757, 764, 763, 770, 769, 776, 775, 782, 781, 788, 787, 794, 793, 800, 799, 806,
805, 812, 811, 861, 860, 736, 735, 742, 741, 748, 747, 754, 753, 760, 759, 766, 765, 772, 771, 778, 777, 784, 783, 790, 789, 796, 795, 802, 801, 808, 807, 814, 813, 862, 0, 738, 737, 744,
743, 750, 749, 756, 755, 762, 761, 768, 767, 774, 773, 780, 779, 786, 785, 792, 791, 798, 797, 804, 803, 810, 809, 816, 815, 864, 863 };
 
/**
* ASCII character to Code Set mapping, from ISO/IEC 16023 Appendix A. 1 = Set A, 2 = Set B, 3 =
* Set C, 4 = Set D, 5 = Set E. 0 refers to special characters that fit into more than one set
* (e.g. GS).
*/
private static final int[] MAXICODE_SET = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 5, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 4, 5, 3, 4, 3, 5, 5, 4, 4,
3, 3, 3, 4, 3, 5, 4, 4, 3, 3, 4, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 };
 
/** ASCII character to symbol value, from ISO/IEC 16023 Appendix A. */
private static final int[] MAXICODE_SYMBOL_CHAR = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 30, 28, 29, 30, 35, 32, 53, 34, 35, 36, 37,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 37, 38, 39, 40, 41, 52, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 42, 43, 44, 45, 46, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 54, 34, 35, 36, 48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 36, 37, 37, 38, 39, 40, 41, 42, 43, 38, 44, 37, 39, 38, 45, 46, 40, 41, 39, 40, 41, 42, 42, 47,
43, 44, 43, 44, 45, 45, 46, 47, 46, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 33, 34, 35, 36, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 32, 33, 34, 35, 36 };
 
private int mode;
private int structuredAppendPosition = 1;
private int structuredAppendTotal = 1;
private String primaryData = "";
private int[] codewords;
private final int[] set = new int[144];
private final int[] character = new int[144];
private final boolean[][] grid = new boolean[33][30];
 
/**
* Sets the MaxiCode mode to use. Only modes 2 to 6 are supported.
*
* @param mode the MaxiCode mode to use
*/
public void setMode(final int mode) {
if (mode < 2 || mode > 6) {
throw new IllegalArgumentException("Invalid MaxiCode mode: " + mode);
}
this.mode = mode;
}
 
/**
* Returns the MaxiCode mode being used. Only modes 2 to 6 are supported.
*
* @return the MaxiCode mode being used
*/
public int getMode() {
return this.mode;
}
 
/**
* If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured
* format, this method sets the position of this symbol in the series. Valid values are 1
* through 8 inclusive.
*
* @param position the position of this MaxiCode symbol in the structured append series
*/
public void setStructuredAppendPosition(final int position) {
if (position < 1 || position > 8) {
throw new IllegalArgumentException("Invalid MaxiCode structured append position: " + position);
}
this.structuredAppendPosition = position;
}
 
/**
* Returns the position of this MaxiCode symbol in a series of symbols using structured append.
* If this symbol is not part of such a series, this method will return <code>1</code>.
*
* @return the position of this MaxiCode symbol in a series of symbols using structured append
*/
public int getStructuredAppendPosition() {
return this.structuredAppendPosition;
}
 
/**
* If this MaxiCode symbol is part of a series of MaxiCode symbols appended in a structured
* format, this method sets the total number of symbols in the series. Valid values are 1
* through 8 inclusive. A value of 1 indicates that this symbol is not part of a structured
* append series.
*
* @param total the total number of MaxiCode symbols in the structured append series
*/
public void setStructuredAppendTotal(final int total) {
if (total < 1 || total > 8) {
throw new IllegalArgumentException("Invalid MaxiCode structured append total: " + total);
}
this.structuredAppendTotal = total;
}
 
/**
* Returns the size of the series of MaxiCode symbols using structured append that this symbol
* is part of. If this symbol is not part of a structured append series, this method will return
* <code>1</code>.
*
* @return size of the series that this symbol is part of
*/
public int getStructuredAppendTotal() {
return this.structuredAppendTotal;
}
 
/**
* Sets the primary data. Should only be used for modes 2 and 3. Must conform to the following
* structure:
*
* <table summary="Expected primary data structure.">
* <tr>
* <th>Characters</th>
* <th>Meaning</th>
* </tr>
* <tr>
* <td>1-9</td>
* <td>Postal code data which can consist of up to 9 digits (for mode 2) or up to 6 alphanumeric
* characters (for mode 3). Remaining unused characters should be filled with the SPACE
* character (ASCII 32).</td>
* </tr>
* <tr>
* <td>10-12</td>
* <td>Three-digit country code according to ISO-3166.</td>
* </tr>
* <tr>
* <td>13-15</td>
* <td>Three digit service code. This depends on your parcel courier.</td>
* </tr>
* </table>
*
* @param primary the primary data
*/
public void setPrimary(final String primary) {
this.primaryData = primary;
}
 
/**
* Returns the primary data for this MaxiCode symbol. Should only be used for modes 2 and 3.
*
* @return the primary data for this MaxiCode symbol
*/
public String getPrimary() {
return this.primaryData;
}
 
/** {@inheritDoc} */
@Override
protected void encode() {
 
eciProcess();
 
// mode 2 -> mode 3 if postal code isn't strictly numeric
if (this.mode == 2) {
for (int i = 0; i < 10 && i < this.primaryData.length(); i++) {
if (this.primaryData.charAt(i) < '0' || this.primaryData.charAt(i) > '9') {
this.mode = 3;
break;
}
}
}
 
// initialize the set and character arrays
processText();
 
// start building the codeword array, starting with a copy of the character data
// insert primary message if this is a structured carrier message; insert mode otherwise
this.codewords = Arrays.copyOf(this.character, this.character.length);
if (this.mode == 2 || this.mode == 3) {
final int[] primary = getPrimaryCodewords();
this.codewords = insertArray(this.codewords, 0, primary);
} else {
this.codewords = insertArray(this.codewords, 0, new int[] { this.mode });
}
 
// insert structured append flag if necessary
if (this.structuredAppendTotal > 1) {
 
final int[] flag = new int[2];
flag[0] = 33; // padding
flag[1] = this.structuredAppendPosition - 1 << 3 | this.structuredAppendTotal - 1; // position
// +
// total
 
int index;
if (this.mode == 2 || this.mode == 3) {
index = 10; // first two data symbols in the secondary message
} else {
index = 1; // first two data symbols in the primary message (first symbol at index 0
// isn't a data symbol)
}
 
this.codewords = insertArray(this.codewords, index, flag);
}
 
int secondaryMax, secondaryECMax;
if (this.mode == 5) {
// 68 data codewords, 56 error corrections in secondary message
secondaryMax = 68;
secondaryECMax = 56;
} else {
// 84 data codewords, 40 error corrections in secondary message
secondaryMax = 84;
secondaryECMax = 40;
}
 
// truncate data codewords to maximum data space available
final int totalMax = secondaryMax + 10;
if (this.codewords.length > totalMax) {
this.codewords = Arrays.copyOfRange(this.codewords, 0, totalMax);
}
 
// insert primary error correction between primary message and secondary message (always
// EEC)
final int[] primary = Arrays.copyOfRange(this.codewords, 0, 10);
final int[] primaryCheck = getErrorCorrection(primary, 10);
this.codewords = insertArray(this.codewords, 10, primaryCheck);
 
// calculate secondary error correction
final int[] secondary = Arrays.copyOfRange(this.codewords, 20, this.codewords.length);
final int[] secondaryOdd = new int[secondary.length / 2];
final int[] secondaryEven = new int[secondary.length / 2];
for (int i = 0; i < secondary.length; i++) {
if ((i & 1) != 0) { // odd
secondaryOdd[(i - 1) / 2] = secondary[i];
} else { // even
secondaryEven[i / 2] = secondary[i];
}
}
final int[] secondaryECOdd = getErrorCorrection(secondaryOdd, secondaryECMax / 2);
final int[] secondaryECEven = getErrorCorrection(secondaryEven, secondaryECMax / 2);
 
// add secondary error correction after secondary message
this.codewords = Arrays.copyOf(this.codewords, this.codewords.length + secondaryECOdd.length + secondaryECEven.length);
for (int i = 0; i < secondaryECOdd.length; i++) {
this.codewords[20 + secondaryMax + 2 * i + 1] = secondaryECOdd[i];
}
for (int i = 0; i < secondaryECEven.length; i++) {
this.codewords[20 + secondaryMax + 2 * i] = secondaryECEven[i];
}
 
infoLine("Mode: " + this.mode);
infoLine("ECC Codewords: " + secondaryECMax);
info("Codewords: ");
for (int i = 0; i < this.codewords.length; i++) {
infoSpace(this.codewords[i]);
}
infoLine();
 
// copy data into symbol grid
final int[] bit_pattern = new int[7];
for (int i = 0; i < 33; i++) {
for (int j = 0; j < 30; j++) {
 
final int block = (MAXICODE_GRID[i * 30 + j] + 5) / 6;
final int bit = (MAXICODE_GRID[i * 30 + j] + 5) % 6;
 
if (block != 0) {
 
bit_pattern[0] = (this.codewords[block - 1] & 0x20) >> 5;
bit_pattern[1] = (this.codewords[block - 1] & 0x10) >> 4;
bit_pattern[2] = (this.codewords[block - 1] & 0x8) >> 3;
bit_pattern[3] = (this.codewords[block - 1] & 0x4) >> 2;
bit_pattern[4] = (this.codewords[block - 1] & 0x2) >> 1;
bit_pattern[5] = this.codewords[block - 1] & 0x1;
 
if (bit_pattern[bit] != 0) {
this.grid[i][j] = true;
} else {
this.grid[i][j] = false;
}
}
}
}
 
// add orientation markings
this.grid[0][28] = true; // top right filler
this.grid[0][29] = true;
this.grid[9][10] = true; // top left marker
this.grid[9][11] = true;
this.grid[10][11] = true;
this.grid[15][7] = true; // left hand marker
this.grid[16][8] = true;
this.grid[16][20] = true; // right hand marker
this.grid[17][20] = true;
this.grid[22][10] = true; // bottom left marker
this.grid[23][10] = true;
this.grid[22][17] = true; // bottom right marker
this.grid[23][17] = true;
 
// the following is provided for compatibility, but the results are not useful
this.row_count = 33;
this.readable = "";
this.pattern = new String[33];
this.row_height = new int[33];
for (int i = 0; i < 33; i++) {
final StringBuilder bin = new StringBuilder(30);
for (int j = 0; j < 30; j++) {
if (this.grid[i][j]) {
bin.append("1");
} else {
bin.append("0");
}
}
this.pattern[i] = bin2pat(bin);
this.row_height[i] = 1;
}
this.symbol_height = 72;
this.symbol_width = 74;
}
 
/**
* Extracts the postal code, country code and service code from the primary data and returns the
* corresponding primary message codewords.
*
* @return the primary message codewords
*/
private int[] getPrimaryCodewords() {
 
assert this.mode == 2 || this.mode == 3;
 
if (this.primaryData.length() != 15) {
throw new OkapiException("Invalid Primary String");
}
 
for (int i = 9; i < 15; i++) { /* check that country code and service are numeric */
if (this.primaryData.charAt(i) < '0' || this.primaryData.charAt(i) > '9') {
throw new OkapiException("Invalid Primary String");
}
}
 
String postcode;
if (this.mode == 2) {
postcode = this.primaryData.substring(0, 9);
final int index = postcode.indexOf(' ');
if (index != -1) {
postcode = postcode.substring(0, index);
}
} else {
assert this.mode == 3;
postcode = this.primaryData.substring(0, 6);
}
 
final int country = Integer.parseInt(this.primaryData.substring(9, 12));
final int service = Integer.parseInt(this.primaryData.substring(12, 15));
 
infoLine("Postal Code: " + postcode);
infoLine("Country Code: " + country);
infoLine("Service: " + service);
 
if (this.mode == 2) {
return getMode2PrimaryCodewords(postcode, country, service);
} else {
assert this.mode == 3;
return getMode3PrimaryCodewords(postcode, country, service);
}
}
 
/**
* Returns the primary message codewords for mode 2.
*
* @param postcode the postal code
* @param country the country code
* @param service the service code
* @return the primary message, as codewords
*/
private static int[] getMode2PrimaryCodewords(String postcode, final int country, final int service) {
 
for (int i = 0; i < postcode.length(); i++) {
if (postcode.charAt(i) < '0' || postcode.charAt(i) > '9') {
postcode = postcode.substring(0, i);
break;
}
}
 
final int postcodeNum = Integer.parseInt(postcode);
 
final int[] primary = new int[10];
primary[0] = (postcodeNum & 0x03) << 4 | 2;
primary[1] = (postcodeNum & 0xfc) >> 2;
primary[2] = (postcodeNum & 0x3f00) >> 8;
primary[3] = (postcodeNum & 0xfc000) >> 14;
primary[4] = (postcodeNum & 0x3f00000) >> 20;
primary[5] = (postcodeNum & 0x3c000000) >> 26 | (postcode.length() & 0x3) << 4;
primary[6] = (postcode.length() & 0x3c) >> 2 | (country & 0x3) << 4;
primary[7] = (country & 0xfc) >> 2;
primary[8] = (country & 0x300) >> 8 | (service & 0xf) << 2;
primary[9] = (service & 0x3f0) >> 4;
 
return primary;
}
 
/**
* Returns the primary message codewords for mode 3.
*
* @param postcode the postal code
* @param country the country code
* @param service the service code
* @return the primary message, as codewords
*/
private static int[] getMode3PrimaryCodewords(String postcode, final int country, final int service) {
 
final int[] postcodeNums = new int[postcode.length()];
 
postcode = postcode.toUpperCase();
for (int i = 0; i < postcodeNums.length; i++) {
postcodeNums[i] = postcode.charAt(i);
if (postcode.charAt(i) >= 'A' && postcode.charAt(i) <= 'Z') {
// (Capital) letters shifted to Code Set A values
postcodeNums[i] -= 64;
}
if (postcodeNums[i] == 27 || postcodeNums[i] == 31 || postcodeNums[i] == 33 || postcodeNums[i] >= 59) {
// Not a valid postal code character, use space instead
postcodeNums[i] = 32;
}
// Input characters lower than 27 (NUL - SUB) in postal code are interpreted as capital
// letters in Code Set A (e.g. LF becomes 'J')
}
 
final int[] primary = new int[10];
primary[0] = (postcodeNums[5] & 0x03) << 4 | 3;
primary[1] = (postcodeNums[4] & 0x03) << 4 | (postcodeNums[5] & 0x3c) >> 2;
primary[2] = (postcodeNums[3] & 0x03) << 4 | (postcodeNums[4] & 0x3c) >> 2;
primary[3] = (postcodeNums[2] & 0x03) << 4 | (postcodeNums[3] & 0x3c) >> 2;
primary[4] = (postcodeNums[1] & 0x03) << 4 | (postcodeNums[2] & 0x3c) >> 2;
primary[5] = (postcodeNums[0] & 0x03) << 4 | (postcodeNums[1] & 0x3c) >> 2;
primary[6] = (postcodeNums[0] & 0x3c) >> 2 | (country & 0x3) << 4;
primary[7] = (country & 0xfc) >> 2;
primary[8] = (country & 0x300) >> 8 | (service & 0xf) << 2;
primary[9] = (service & 0x3f0) >> 4;
 
return primary;
}
 
/**
* Formats text according to Appendix A, populating the {@link #set} and {@link #character}
* arrays.
*
* @return true if the content fits in this symbol and was formatted; false otherwise
*/
private void processText() {
 
int length = this.content.length();
int i, j, count, current_set;
 
if (length > 138) {
throw new OkapiException("Input data too long");
}
 
for (i = 0; i < 144; i++) {
this.set[i] = -1;
this.character[i] = 0;
}
 
for (i = 0; i < length; i++) {
/*
* Look up characters in table from Appendix A - this gives value and code set for most
* characters
*/
this.set[i] = MAXICODE_SET[this.inputData[i]];
this.character[i] = MAXICODE_SYMBOL_CHAR[this.inputData[i]];
}
 
// If a character can be represented in more than one code set, pick which version to use.
if (this.set[0] == 0) {
if (this.character[0] == 13) {
this.character[0] = 0;
}
this.set[0] = 1;
}
 
for (i = 1; i < length; i++) {
if (this.set[i] == 0) {
/* Special character that can be represented in more than one code set. */
if (this.character[i] == 13) {
/* Carriage Return */
this.set[i] = bestSurroundingSet(i, length, 1, 5);
if (this.set[i] == 5) {
this.character[i] = 13;
} else {
this.character[i] = 0;
}
} else if (this.character[i] == 28) {
/* FS */
this.set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5);
if (this.set[i] == 5) {
this.character[i] = 32;
}
} else if (this.character[i] == 29) {
/* GS */
this.set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5);
if (this.set[i] == 5) {
this.character[i] = 33;
}
} else if (this.character[i] == 30) {
/* RS */
this.set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5);
if (this.set[i] == 5) {
this.character[i] = 34;
}
} else if (this.character[i] == 32) {
/* Space */
this.set[i] = bestSurroundingSet(i, length, 1, 2, 3, 4, 5);
if (this.set[i] == 1) {
this.character[i] = 32;
} else if (this.set[i] == 2) {
this.character[i] = 47;
} else {
this.character[i] = 59;
}
} else if (this.character[i] == 44) {
/* Comma */
this.set[i] = bestSurroundingSet(i, length, 1, 2);
if (this.set[i] == 2) {
this.character[i] = 48;
}
} else if (this.character[i] == 46) {
/* Full Stop */
this.set[i] = bestSurroundingSet(i, length, 1, 2);
if (this.set[i] == 2) {
this.character[i] = 49;
}
} else if (this.character[i] == 47) {
/* Slash */
this.set[i] = bestSurroundingSet(i, length, 1, 2);
if (this.set[i] == 2) {
this.character[i] = 50;
}
} else if (this.character[i] == 58) {
/* Colon */
this.set[i] = bestSurroundingSet(i, length, 1, 2);
if (this.set[i] == 2) {
this.character[i] = 51;
}
}
}
}
 
for (i = length; i < this.set.length; i++) {
/* Add the padding */
if (this.set[length - 1] == 2) {
this.set[i] = 2;
} else {
this.set[i] = 1;
}
this.character[i] = 33;
}
 
/*
* Find candidates for number compression (not allowed in primary message in modes 2 and 3).
*/
if (this.mode == 2 || this.mode == 3) {
j = 9;
} else {
j = 0;
}
count = 0;
for (i = j; i < 143; i++) {
if (this.set[i] == 1 && this.character[i] >= 48 && this.character[i] <= 57) {
/* Character is a number */
count++;
} else {
count = 0;
}
if (count == 9) {
/* Nine digits in a row can be compressed */
this.set[i] = 6;
this.set[i - 1] = 6;
this.set[i - 2] = 6;
this.set[i - 3] = 6;
this.set[i - 4] = 6;
this.set[i - 5] = 6;
this.set[i - 6] = 6;
this.set[i - 7] = 6;
this.set[i - 8] = 6;
count = 0;
}
}
 
/* Add shift and latch characters */
current_set = 1;
i = 0;
do {
if (this.set[i] != current_set && this.set[i] != 6) {
switch (this.set[i]) {
case 1:
if (i + 1 < this.set.length && this.set[i + 1] == 1) {
if (i + 2 < this.set.length && this.set[i + 2] == 1) {
if (i + 3 < this.set.length && this.set[i + 3] == 1) {
/* Latch A */
insert(i, 63);
current_set = 1;
length++;
i += 3;
} else {
/* 3 Shift A */
insert(i, 57);
length++;
i += 2;
}
} else {
/* 2 Shift A */
insert(i, 56);
length++;
i++;
}
} else {
/* Shift A */
insert(i, 59);
length++;
}
break;
case 2:
if (i + 1 < this.set.length && this.set[i + 1] == 2) {
/* Latch B */
insert(i, 63);
current_set = 2;
length++;
i++;
} else {
/* Shift B */
insert(i, 59);
length++;
}
break;
case 3:
if (i + 3 < this.set.length && this.set[i + 1] == 3 && this.set[i + 2] == 3 && this.set[i + 3] == 3) {
/* Lock In C */
insert(i, 60);
insert(i, 60);
current_set = 3;
length++;
i += 3;
} else {
/* Shift C */
insert(i, 60);
length++;
}
break;
case 4:
if (i + 3 < this.set.length && this.set[i + 1] == 4 && this.set[i + 2] == 4 && this.set[i + 3] == 4) {
/* Lock In D */
insert(i, 61);
insert(i, 61);
current_set = 4;
length++;
i += 3;
} else {
/* Shift D */
insert(i, 61);
length++;
}
break;
case 5:
if (i + 3 < this.set.length && this.set[i + 1] == 5 && this.set[i + 2] == 5 && this.set[i + 3] == 5) {
/* Lock In E */
insert(i, 62);
insert(i, 62);
current_set = 5;
length++;
i += 3;
} else {
/* Shift E */
insert(i, 62);
length++;
}
break;
default:
throw new OkapiException("Unexpected set " + this.set[i] + " at index " + i + ".");
}
i++;
}
i++;
} while (i < this.set.length);
 
/* Number compression has not been forgotten! It's handled below. */
i = 0;
do {
if (this.set[i] == 6) {
/* Number compression */
int value = 0;
for (j = 0; j < 9; j++) {
value *= 10;
value += this.character[i + j] - '0';
}
this.character[i] = 31; /* NS */
this.character[i + 1] = (value & 0x3f000000) >> 24;
this.character[i + 2] = (value & 0xfc0000) >> 18;
this.character[i + 3] = (value & 0x3f000) >> 12;
this.character[i + 4] = (value & 0xfc0) >> 6;
this.character[i + 5] = value & 0x3f;
i += 6;
for (j = i; j < 140; j++) {
this.set[j] = this.set[j + 3];
this.character[j] = this.character[j + 3];
}
length -= 3;
} else {
i++;
}
} while (i < this.set.length);
 
/* Inject ECI codes to beginning of data, according to Table 3 */
if (this.eciMode != 3) {
insert(0, 27); // ECI
 
if (this.eciMode >= 0 && this.eciMode <= 31) {
insert(1, this.eciMode & 0x1F);
length += 2;
}
 
if (this.eciMode >= 32 && this.eciMode <= 1023) {
insert(1, 0x20 + (this.eciMode >> 6));
insert(2, this.eciMode & 0x3F);
length += 3;
}
 
if (this.eciMode >= 1024 && this.eciMode <= 32767) {
insert(1, 0x30 + (this.eciMode >> 12));
insert(2, this.eciMode >> 6 & 0x3F);
insert(3, this.eciMode & 0x3F);
length += 4;
}
 
if (this.eciMode >= 32768 && this.eciMode <= 999999) {
insert(1, 0x38 + (this.eciMode >> 18));
insert(2, this.eciMode >> 12 & 0x3F);
insert(3, this.eciMode >> 6 & 0x3F);
insert(4, this.eciMode & 0x3F);
length += 5;
}
}
 
/* Make sure we haven't exceeded the maximum data length. */
int maxLength;
if (this.mode == 2 || this.mode == 3) {
maxLength = 84;
} else if (this.mode == 4 || this.mode == 6) {
maxLength = 93;
} else if (this.mode == 5) {
maxLength = 77;
} else {
maxLength = 0; // impossible
}
if (length > maxLength) {
throw new OkapiException("Input data too long");
}
}
 
/**
* Guesses the best set to use at the specified index by looking at the surrounding sets. In
* general, characters in lower-numbered sets are more common, so we choose them if we can. If
* no good surrounding sets can be found, the default value returned is the first value from the
* valid set.
*
* @param index the current index
* @param length the maximum length to look at
* @param valid the valid sets for this index
* @return the best set to use at the specified index
*/
private int bestSurroundingSet(final int index, final int length, final int... valid) {
final int option1 = this.set[index - 1];
if (index + 1 < length) {
// we have two options to check
final int option2 = this.set[index + 1];
if (contains(valid, option1) && contains(valid, option2)) {
return Math.min(option1, option2);
} else if (contains(valid, option1)) {
return option1;
} else if (contains(valid, option2)) {
return option2;
} else {
return valid[0];
}
} else {
// we only have one option to check
if (contains(valid, option1)) {
return option1;
} else {
return valid[0];
}
}
}
 
/**
* Moves everything up so that the specified shift or latch character can be inserted.
*
* @param position the position beyond which everything needs to be shifted
* @param c the latch or shift character to insert at the specified position, after everything
* has been shifted
*/
private void insert(final int position, final int c) {
for (int i = 143; i > position; i--) {
this.set[i] = this.set[i - 1];
this.character[i] = this.character[i - 1];
}
this.character[position] = c;
}
 
/**
* Returns the error correction codewords for the specified data codewords.
*
* @param codewords the codewords that we need error correction codewords for
* @param ecclen the number of error correction codewords needed
* @return the error correction codewords for the specified data codewords
*/
private static int[] getErrorCorrection(final int[] codewords, final int ecclen) {
 
final ReedSolomon rs = new ReedSolomon();
rs.init_gf(0x43);
rs.init_code(ecclen, 1);
rs.encode(codewords.length, codewords);
 
final int[] results = new int[ecclen];
for (int i = 0; i < ecclen; i++) {
results[i] = rs.getResult(results.length - 1 - i);
}
 
return results;
}
 
/** {@inheritDoc} */
@Override
protected void plotSymbol() {
 
// hexagons
for (int row = 0; row < 33; row++) {
for (int col = 0; col < 30; col++) {
if (this.grid[row][col]) {
double x = 2.46 * col + 1.23;
if ((row & 1) != 0) {
x += 1.23;
}
final double y = 2.135 * row + 1.43;
this.hexagons.add(new Hexagon(x, y));
}
}
}
 
// circles
final double[] radii = { 10.85, 8.97, 7.10, 5.22, 3.31, 1.43 };
for (int i = 0; i < radii.length; i++) {
final Ellipse2D.Double circle = new Ellipse2D.Double();
circle.setFrameFromCenter(35.76, 35.60, 35.76 + radii[i], 35.60 + radii[i]);
this.target.add(circle);
}
}
 
/** {@inheritDoc} */
@Override
protected int[] getCodewords() {
return this.codewords;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Hexagon.java
New file
0,0 → 1,41
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
/**
* Calculate a set of points to make a hexagon
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Hexagon {
 
private static final double INK_SPREAD = 1.25;
 
private static final double[] OFFSET_X = { 0.0, 0.86, 0.86, 0.0, -0.86, -0.86 };
private static final double[] OFFSET_Y = { 1.0, 0.5, -0.5, -1.0, -0.5, 0.5 };
 
public final double centreX;
public final double centreY;
public final double[] pointX = new double[6];
public final double[] pointY = new double[6];
 
public Hexagon(final double centreX, final double centreY) {
this.centreX = centreX;
this.centreY = centreY;
for (int i = 0; i < 6; i++) {
this.pointX[i] = centreX + OFFSET_X[i] * INK_SPREAD;
this.pointY[i] = centreY + OFFSET_Y[i] * INK_SPREAD;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code16k.java
New file
0,0 → 1,779
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import java.awt.geom.Rectangle2D;
import java.nio.charset.StandardCharsets;
 
/**
* <p>
* Implements Code 16K symbology according to BS EN 12323:2005.
*
* <p>
* Encodes using a stacked symbology based on Code 128. Supports encoding of any 8-bit ISO 8859-1
* (Latin-1) data with a maximum data capacity of 77 alpha-numeric characters or 154 numerical
* digits.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Code16k extends Symbol {
 
private enum Mode {
NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB
}
 
/* EN 12323 Table 1 - "Code 16K" character encodations */
private static final String[] C16K_TABLE = { "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231",
"113222", "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121",
"111323", "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131",
"213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221",
"112214", "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212",
"421112", "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214",
"211232", "211133" };
 
/* EN 12323 Table 3 and Table 4 - Start patterns and stop patterns */
private static final String[] C16K_START_STOP = { "3211", "2221", "2122", "1411", "1132", "1231", "1114", "3112" };
 
/* EN 12323 Table 5 - Start and stop values defining row numbers */
private static final int[] C16K_START_VALUES = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 };
private static final int[] C16K_STOP_VALUES = { 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 0, 1, 2, 3 };
 
private final Mode[] block_mode = new Mode[170]; /* RENAME block_mode */
private final int[] block_length = new int[170]; /* RENAME block_length */
private int block_count;
 
@Override
protected boolean gs1Supported() {
return true;
}
 
@Override
protected void encode() {
 
// TODO: is it possible to share any of this code with Code128, which is more up to date?
 
String width_pattern;
int current_row, rows_needed, first_check, second_check;
int indexchaine, pads_needed;
char[] set, fset;
Mode mode;
char last_set, current_set;
int i, j, k, m, read;
int[] values;
int bar_characters;
double glyph_count;
int first_sum, second_sum;
int input_length;
int c_count;
boolean f_state;
 
if (!this.content.matches("[\u0000-\u00FF]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
this.inputData = toBytes(this.content, StandardCharsets.ISO_8859_1);
input_length = this.inputData.length;
 
bar_characters = 0;
set = new char[160];
fset = new char[160];
values = new int[160];
 
if (input_length > 157) {
throw new OkapiException("Input too long");
}
 
/* Detect extended ASCII characters */
for (i = 0; i < input_length; i++) {
if (this.inputData[i] >= 128) {
fset[i] = 'f';
} else {
fset[i] = ' ';
}
}
 
/* Decide when to latch to extended mode */
for (i = 0; i < input_length; i++) {
j = 0;
if (fset[i] == 'f') {
do {
j++;
} while (fset[i + j] == 'f');
if (j >= 5 || j >= 3 && i + j == input_length - 1) {
for (k = 0; k <= j; k++) {
fset[i + k] = 'F';
}
}
}
}
 
/* Decide if it is worth reverting to 646 encodation for a few characters */
if (input_length > 1) {
for (i = 1; i < input_length; i++) {
if (fset[i - 1] == 'F' && fset[i] == ' ') {
/* Detected a change from 8859-1 to 646 - count how long for */
for (j = 0; fset[i + j] == ' ' && i + j < input_length; j++) {
;
}
if (j < 5 || j < 3 && i + j == input_length - 1) {
/* Change to shifting back rather than latching back */
for (k = 0; k < j; k++) {
fset[i + k] = 'n';
}
}
}
}
}
 
/* Detect mode A, B and C characters */
this.block_count = 0;
indexchaine = 0;
 
mode = findSubset(this.inputData[indexchaine]);
if (this.inputData[indexchaine] == FNC1) {
mode = Mode.ABORC;
} /* FNC1 */
 
for (i = 0; i < 160; i++) {
this.block_length[i] = 0;
}
 
do {
this.block_mode[this.block_count] = mode;
while (this.block_mode[this.block_count] == mode && indexchaine < input_length) {
this.block_length[this.block_count]++;
indexchaine++;
if (indexchaine < input_length) {
mode = findSubset(this.inputData[indexchaine]);
if (this.inputData[indexchaine] == FNC1) {
mode = Mode.ABORC;
} /* FNC1 */
}
}
this.block_count++;
} while (indexchaine < input_length);
 
reduceSubsetChanges(this.block_count);
 
/* Put set data into set[] */
read = 0;
for (i = 0; i < this.block_count; i++) {
for (j = 0; j < this.block_length[i]; j++) {
switch (this.block_mode[i]) {
case SHIFTA:
set[read] = 'a';
break;
case LATCHA:
set[read] = 'A';
break;
case SHIFTB:
set[read] = 'b';
break;
case LATCHB:
set[read] = 'B';
break;
case LATCHC:
set[read] = 'C';
break;
}
read++;
}
}
 
/* Adjust for strings which start with shift characters - make them latch instead */
if (set[0] == 'a') {
i = 0;
do {
set[i] = 'A';
i++;
} while (set[i] == 'a');
}
 
if (set[0] == 'b') {
i = 0;
do {
set[i] = 'B';
i++;
} while (set[i] == 'b');
}
 
/* Watch out for odd-length Mode C blocks */
c_count = 0;
for (i = 0; i < read; i++) {
if (set[i] == 'C') {
if (this.inputData[i] == FNC1) {
if ((c_count & 1) != 0) {
if (i - c_count != 0) {
set[i - c_count] = 'B';
} else {
set[i - 1] = 'B';
}
}
c_count = 0;
} else {
c_count++;
}
} else {
if ((c_count & 1) != 0) {
if (i - c_count != 0) {
set[i - c_count] = 'B';
} else {
set[i - 1] = 'B';
}
}
c_count = 0;
}
}
if ((c_count & 1) != 0) {
if (i - c_count != 0) {
set[i - c_count] = 'B';
} else {
set[i - 1] = 'B';
}
}
for (i = 1; i < read - 1; i++) {
if (set[i] == 'C' && set[i - 1] == 'B' && set[i + 1] == 'B') {
set[i] = 'B';
}
}
 
/* Make sure the data will fit in the symbol */
last_set = ' ';
glyph_count = 0.0;
for (i = 0; i < input_length; i++) {
if (set[i] == 'a' || set[i] == 'b') {
glyph_count = glyph_count + 1.0;
}
if (fset[i] == 'f' || fset[i] == 'n') {
glyph_count = glyph_count + 1.0;
}
if (set[i] == 'A' || set[i] == 'B' || set[i] == 'C') {
if (set[i] != last_set) {
last_set = set[i];
glyph_count = glyph_count + 1.0;
}
}
if (i == 0) {
if (set[i] == 'B' && set[1] == 'C') {
glyph_count = glyph_count - 1.0;
}
if (set[i] == 'B' && set[1] == 'B' && set[2] == 'C') {
glyph_count = glyph_count - 1.0;
}
if (fset[i] == 'F') {
glyph_count = glyph_count + 2.0;
}
} else {
if (fset[i] == 'F' && fset[i - 1] != 'F') {
glyph_count = glyph_count + 2.0;
}
if (fset[i] != 'F' && fset[i - 1] == 'F') {
glyph_count = glyph_count + 2.0;
}
}
 
if (set[i] == 'C' && this.inputData[i] != FNC1) {
glyph_count = glyph_count + 0.5;
} else {
glyph_count = glyph_count + 1.0;
}
}
 
if (this.inputDataType == DataType.GS1 && set[0] != 'A') {
/* FNC1 can be integrated with mode character */
glyph_count--;
}
 
if (glyph_count > 77.0) {
throw new OkapiException("Input too long");
}
 
/* Calculate how tall the symbol will be */
glyph_count = glyph_count + 2.0;
i = (int) glyph_count;
rows_needed = i / 5;
if (i % 5 > 0) {
rows_needed++;
}
 
if (rows_needed == 1) {
rows_needed = 2;
}
 
/* start with the mode character - Table 2 */
m = 0;
switch (set[0]) {
case 'A':
m = 0;
break;
case 'B':
m = 1;
break;
case 'C':
m = 2;
break;
}
 
if (this.readerInit) {
if (m == 2) {
m = 5;
}
if (this.inputDataType == DataType.GS1) {
throw new OkapiException("Cannot use both GS1 mode and Reader Initialisation");
} else {
if (set[0] == 'B' && set[1] == 'C') {
m = 6;
}
}
values[bar_characters] = 7 * (rows_needed - 2) + m; /* see 4.3.4.2 */
values[bar_characters + 1] = 96; /* FNC3 */
bar_characters += 2;
} else {
if (this.inputDataType == DataType.GS1) {
/* Integrate FNC1 */
switch (set[0]) {
case 'B':
m = 3;
break;
case 'C':
m = 4;
break;
}
} else {
if (set[0] == 'B' && set[1] == 'C') {
m = 5;
}
if (set[0] == 'B' && set[1] == 'B' && set[2] == 'C') {
m = 6;
}
}
}
values[bar_characters] = 7 * (rows_needed - 2) + m; /* see 4.3.4.2 */
bar_characters++;
// }
current_set = set[0];
f_state = false;
/*
* f_state remembers if we are in Extended ASCII mode (value 1) or in ISO/IEC 646 mode
* (value 0)
*/
if (fset[0] == 'F') {
switch (current_set) {
case 'A':
values[bar_characters] = 101;
values[bar_characters + 1] = 101;
break;
case 'B':
values[bar_characters] = 100;
values[bar_characters + 1] = 100;
break;
}
bar_characters += 2;
f_state = true;
}
 
read = 0;
 
/* Encode the data */
do {
 
if (read != 0 && set[read] != set[read - 1]) { /* Latch different code set */
switch (set[read]) {
case 'A':
values[bar_characters] = 101;
bar_characters++;
current_set = 'A';
break;
case 'B':
values[bar_characters] = 100;
bar_characters++;
current_set = 'B';
break;
case 'C':
if (!(read == 1 && set[0] == 'B')) { /* Not Mode C/Shift B */
if (!(read == 2 && set[0] == 'B' && set[1] == 'B')) {
/* Not Mode C/Double Shift B */
values[bar_characters] = 99;
bar_characters++;
}
}
current_set = 'C';
break;
}
}
if (read != 0) {
if (fset[read] == 'F' && !f_state) {
/* Latch beginning of extended mode */
switch (current_set) {
case 'A':
values[bar_characters] = 101;
values[bar_characters + 1] = 101;
break;
case 'B':
values[bar_characters] = 100;
values[bar_characters + 1] = 100;
break;
}
bar_characters += 2;
f_state = true;
}
if (fset[read] == ' ' && f_state) {
/* Latch end of extended mode */
switch (current_set) {
case 'A':
values[bar_characters] = 101;
values[bar_characters + 1] = 101;
break;
case 'B':
values[bar_characters] = 100;
values[bar_characters + 1] = 100;
break;
}
bar_characters += 2;
f_state = false;
}
}
 
if (fset[i] == 'f' || fset[i] == 'n') {
/* Shift extended mode */
switch (current_set) {
case 'A':
values[bar_characters] = 101; /* FNC 4 */
break;
case 'B':
values[bar_characters] = 100; /* FNC 4 */
break;
}
bar_characters++;
}
 
if (set[i] == 'a' || set[i] == 'b') {
/* Insert shift character */
values[bar_characters] = 98;
bar_characters++;
}
 
if (this.inputData[read] != FNC1) {
switch (set[read]) { /* Encode data characters */
case 'A':
case 'a':
getValueSubsetA(this.inputData[read], values, bar_characters);
bar_characters++;
read++;
break;
case 'B':
case 'b':
getValueSubsetB(this.inputData[read], values, bar_characters);
bar_characters++;
read++;
break;
case 'C':
getValueSubsetC(this.inputData[read], this.inputData[read + 1], values, bar_characters);
bar_characters++;
read += 2;
break;
}
} else {
values[bar_characters] = 102;
bar_characters++;
read++;
}
 
} while (read < input_length);
 
pads_needed = 5 - (bar_characters + 2) % 5;
if (pads_needed == 5) {
pads_needed = 0;
}
if (bar_characters + pads_needed < 8) {
pads_needed += 8 - (bar_characters + pads_needed);
}
for (i = 0; i < pads_needed; i++) {
values[bar_characters] = 106;
bar_characters++;
}
 
/* Calculate check digits */
first_sum = 0;
second_sum = 0;
for (i = 0; i < bar_characters; i++) {
first_sum += (i + 2) * values[i];
second_sum += (i + 1) * values[i];
}
first_check = first_sum % 107;
second_sum += first_check * (bar_characters + 1);
second_check = second_sum % 107;
values[bar_characters] = first_check;
values[bar_characters + 1] = second_check;
bar_characters += 2;
 
this.readable = "";
this.pattern = new String[rows_needed];
this.row_count = rows_needed;
this.row_height = new int[rows_needed];
 
infoLine("Symbol Rows: " + rows_needed);
infoLine("First Check Digit: " + first_check);
infoLine("Second Check Digit: " + second_check);
info("Codewords: ");
 
for (current_row = 0; current_row < rows_needed; current_row++) {
 
width_pattern = "";
width_pattern += C16K_START_STOP[C16K_START_VALUES[current_row]];
width_pattern += "1";
for (i = 0; i < 5; i++) {
width_pattern += C16K_TABLE[values[current_row * 5 + i]];
infoSpace(values[current_row * 5 + i]);
}
width_pattern += C16K_START_STOP[C16K_STOP_VALUES[current_row]];
 
this.pattern[current_row] = width_pattern;
this.row_height[current_row] = 10;
}
infoLine();
}
 
private void getValueSubsetA(final int source, final int[] values, final int bar_chars) {
if (source > 127) {
if (source < 160) {
values[bar_chars] = source + 64 - 128;
} else {
values[bar_chars] = source - 32 - 128;
}
} else {
if (source < 32) {
values[bar_chars] = source + 64;
} else {
values[bar_chars] = source - 32;
}
}
}
 
private void getValueSubsetB(final int source, final int[] values, final int bar_chars) {
if (source > 127) {
values[bar_chars] = source - 32 - 128;
} else {
values[bar_chars] = source - 32;
}
}
 
private void getValueSubsetC(final int source_a, final int source_b, final int[] values, final int bar_chars) {
int weight;
 
weight = 10 * Character.getNumericValue(source_a) + Character.getNumericValue(source_b);
values[bar_chars] = weight;
}
 
private Mode findSubset(final int letter) {
Mode mode;
 
if (letter <= 31) {
mode = Mode.SHIFTA;
} else if (letter >= 48 && letter <= 57) {
mode = Mode.ABORC;
} else if (letter <= 95) {
mode = Mode.AORB;
} else if (letter <= 127) {
mode = Mode.SHIFTB;
} else if (letter <= 159) {
mode = Mode.SHIFTA;
} else if (letter <= 223) {
mode = Mode.AORB;
} else {
mode = Mode.SHIFTB;
}
 
return mode;
}
 
private void reduceSubsetChanges(
final int block_count) { /* Implements rules from ISO 15417 Annex E */
int i, length;
Mode current, last, next;
 
for (i = 0; i < block_count; i++) {
current = this.block_mode[i];
length = this.block_length[i];
if (i != 0) {
last = this.block_mode[i - 1];
} else {
last = Mode.NULL;
}
if (i != block_count - 1) {
next = this.block_mode[i + 1];
} else {
next = Mode.NULL;
}
 
if (i == 0) { /* first block */
if (block_count == 1 && length == 2 && current == Mode.ABORC) { /* Rule 1a */
this.block_mode[i] = Mode.LATCHC;
}
if (current == Mode.ABORC) {
if (length >= 4) { /* Rule 1b */
this.block_mode[i] = Mode.LATCHC;
} else {
this.block_mode[i] = Mode.AORB;
current = Mode.AORB;
}
}
if (current == Mode.SHIFTA) { /* Rule 1c */
this.block_mode[i] = Mode.LATCHA;
}
if (current == Mode.AORB && next == Mode.SHIFTA) { /* Rule 1c */
this.block_mode[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.AORB) { /* Rule 1d */
this.block_mode[i] = Mode.LATCHB;
}
} else {
if (current == Mode.ABORC && length >= 4) { /* Rule 3 */
this.block_mode[i] = Mode.LATCHC;
current = Mode.LATCHC;
}
if (current == Mode.ABORC) {
this.block_mode[i] = Mode.AORB;
current = Mode.AORB;
}
if (current == Mode.AORB && last == Mode.LATCHA) {
this.block_mode[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.AORB && last == Mode.LATCHB) {
this.block_mode[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.AORB && next == Mode.SHIFTA) {
this.block_mode[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.AORB && next == Mode.SHIFTB) {
this.block_mode[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.AORB) {
this.block_mode[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.SHIFTA && length > 1) { /* Rule 4 */
this.block_mode[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.SHIFTB && length > 1) { /* Rule 5 */
this.block_mode[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.SHIFTA && last == Mode.LATCHA) {
this.block_mode[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.SHIFTB && last == Mode.LATCHB) {
this.block_mode[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.SHIFTA && last == Mode.LATCHC) {
this.block_mode[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.SHIFTB && last == Mode.LATCHC) {
this.block_mode[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
} /* Rule 2 is implimented elsewhere, Rule 6 is implied */
}
combineSubsetBlocks(block_count);
 
}
 
private void combineSubsetBlocks(int block_count) {
int i, j;
 
/* bring together same type blocks */
if (block_count > 1) {
i = 1;
while (i < block_count) {
if (this.block_mode[i - 1] == this.block_mode[i]) {
/* bring together */
this.block_length[i - 1] = this.block_length[i - 1] + this.block_length[i];
j = i + 1;
 
/* decreace the list */
while (j < block_count) {
this.block_length[j - 1] = this.block_length[j];
this.block_mode[j - 1] = this.block_mode[j];
j++;
}
block_count = block_count - 1;
i--;
}
i++;
}
}
}
 
@Override
protected void plotSymbol() {
int xBlock, yBlock;
int x, y, w, h;
boolean black;
 
this.rectangles.clear();
y = 1;
h = 1;
for (yBlock = 0; yBlock < this.row_count; yBlock++) {
black = true;
x = 15;
for (xBlock = 0; xBlock < this.pattern[yBlock].length(); xBlock++) {
if (black) {
black = false;
w = this.pattern[yBlock].charAt(xBlock) - '0';
if (this.row_height[yBlock] == -1) {
h = this.default_height;
} else {
h = this.row_height[yBlock];
}
if (w != 0 && h != 0) {
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
this.rectangles.add(rect);
}
if (x + w > this.symbol_width) {
this.symbol_width = x + w;
}
} else {
black = true;
}
x += this.pattern[yBlock].charAt(xBlock) - '0';
}
y += h;
if (y > this.symbol_height) {
this.symbol_height = y;
}
/* Add bars between rows */
if (yBlock != this.row_count - 1) {
final Rectangle2D.Double rect = new Rectangle2D.Double(15, y - 1, this.symbol_width - 15, 2);
this.rectangles.add(rect);
}
}
 
/* Add top and bottom binding bars */
final Rectangle2D.Double top = new Rectangle2D.Double(0, 0, this.symbol_width + 15, 2);
this.rectangles.add(top);
final Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, this.symbol_width + 15, 2);
this.rectangles.add(bottom);
this.symbol_width += 15;
this.symbol_height += 1;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Pharmazentralnummer.java
New file
0,0 → 1,76
/*
* Copyright 2015 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
/**
* PZN8 is a Code 39 based symbology used by the pharmaceutical industry in Germany. PZN8 encodes a
* 7 digit number and includes a modulo-10 check digit.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Pharmazentralnummer extends Symbol {
 
/*
* Pharmazentral Nummer is a Code 3 of 9 symbol with an extra check digit. Now generates PZN-8.
*/
 
@Override
protected void encode() {
final int l = this.content.length();
String localstr;
int zeroes, count = 0, check_digit;
final Code3Of9 c = new Code3Of9();
 
if (l > 7) {
throw new OkapiException("Input data too long");
}
 
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in input");
}
 
localstr = "-";
zeroes = 7 - l + 1;
for (int i = 1; i < zeroes; i++) {
localstr += '0';
}
 
localstr += this.content;
 
for (int i = 1; i < 8; i++) {
count += i * Character.getNumericValue(localstr.charAt(i));
}
 
check_digit = count % 11;
if (check_digit == 11) {
check_digit = 0;
}
if (check_digit == 10) {
throw new OkapiException("Not a valid PZN identifier");
}
 
infoLine("Check Digit: " + check_digit);
 
localstr += (char) (check_digit + '0');
 
c.setContent(localstr);
 
this.readable = "PZN" + localstr;
this.pattern = new String[1];
this.pattern[0] = c.pattern[0];
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/MsiPlessey.java
New file
0,0 → 1,205
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
/**
* <p>
* Implements the MSI (Modified Plessey) bar code symbology.
*
* <p>
* MSI Plessey can encode a string of numeric digits and has a range of check digit options.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class MsiPlessey extends Symbol {
 
public enum CheckDigit {
NONE, MOD10, MOD10_MOD10, MOD11, MOD11_MOD10
}
 
private final static String[] MSI_PLESS_TABLE = { "12121212", "12121221", "12122112", "12122121", "12211212", "12211221", "12212112", "12212121", "21121212", "21121221" };
 
private CheckDigit checkDigit = CheckDigit.NONE;
 
/**
* Set the check digit scheme to use. Options are: None, Modulo-10, 2 x Modulo-10, Modulo-11 and
* Modulo-11 &amp; 10.
*
* @param checkDigit the type of check digit to add to symbol
*/
public void setCheckDigit(final CheckDigit checkDigit) {
this.checkDigit = checkDigit;
}
 
/**
* Returns the check digit scheme being used.
*
* @return the check digit scheme being used
*/
public CheckDigit getCheckDigit() {
return this.checkDigit;
}
 
@Override
protected void encode() {
 
String intermediate;
final int length = this.content.length();
int i;
String evenString;
String oddString;
String addupString;
int spacer;
int addup;
int weight;
int checkDigit1;
int checkDigit2;
 
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in input");
}
 
intermediate = "21"; // Start
for (i = 0; i < length; i++) {
intermediate += MSI_PLESS_TABLE[Character.getNumericValue(this.content.charAt(i))];
}
 
this.readable = this.content;
 
if (this.checkDigit == CheckDigit.MOD10 || this.checkDigit == CheckDigit.MOD10_MOD10) {
/* Add Modulo-10 check digit */
evenString = "";
oddString = "";
 
spacer = this.content.length() & 1;
 
for (i = this.content.length() - 1; i >= 0; i--) {
if (spacer == 1) {
if ((i & 1) != 0) {
evenString = this.content.charAt(i) + evenString;
} else {
oddString = this.content.charAt(i) + oddString;
}
} else {
if ((i & 1) != 0) {
oddString = this.content.charAt(i) + oddString;
} else {
evenString = this.content.charAt(i) + evenString;
}
}
}
 
if (oddString.length() == 0) {
addupString = "0";
} else {
addupString = Integer.toString(Integer.parseInt(oddString) * 2);
}
 
addupString += evenString;
 
addup = 0;
for (i = 0; i < addupString.length(); i++) {
addup += addupString.charAt(i) - '0';
}
 
checkDigit1 = 10 - addup % 10;
if (checkDigit1 == 10) {
checkDigit1 = 0;
}
 
intermediate += MSI_PLESS_TABLE[checkDigit1];
this.readable += checkDigit1;
}
 
if (this.checkDigit == CheckDigit.MOD11 || this.checkDigit == CheckDigit.MOD11_MOD10) {
/* Add a Modulo-11 check digit */
weight = 2;
addup = 0;
for (i = this.content.length() - 1; i >= 0; i--) {
addup += (this.content.charAt(i) - '0') * weight;
weight++;
 
if (weight == 8) {
weight = 2;
}
}
 
checkDigit1 = 11 - addup % 11;
 
if (checkDigit1 == 11) {
checkDigit1 = 0;
}
 
this.readable += checkDigit1;
if (checkDigit1 == 10) {
intermediate += MSI_PLESS_TABLE[1];
intermediate += MSI_PLESS_TABLE[0];
} else {
intermediate += MSI_PLESS_TABLE[checkDigit1];
}
}
 
if (this.checkDigit == CheckDigit.MOD10_MOD10 || this.checkDigit == CheckDigit.MOD11_MOD10) {
/* Add a second Modulo-10 check digit */
evenString = "";
oddString = "";
 
spacer = this.readable.length() & 1;
 
for (i = this.readable.length() - 1; i >= 0; i--) {
if (spacer == 1) {
if ((i & 1) != 0) {
evenString = this.readable.charAt(i) + evenString;
} else {
oddString = this.readable.charAt(i) + oddString;
}
} else {
if ((i & 1) != 0) {
oddString = this.readable.charAt(i) + oddString;
} else {
evenString = this.readable.charAt(i) + evenString;
}
}
}
 
if (oddString.length() == 0) {
addupString = "0";
} else {
addupString = Integer.toString(Integer.parseInt(oddString) * 2);
}
 
addupString += evenString;
 
addup = 0;
for (i = 0; i < addupString.length(); i++) {
addup += addupString.charAt(i) - '0';
}
 
checkDigit2 = 10 - addup % 10;
if (checkDigit2 == 10) {
checkDigit2 = 0;
}
 
intermediate += MSI_PLESS_TABLE[checkDigit2];
this.readable += checkDigit2;
}
 
intermediate += "121"; // Stop
 
this.pattern = new String[] { intermediate };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/DataMatrix.java
New file
0,0 → 1,1635
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
import java.util.Arrays;
 
/**
* <p>
* Implements Data Matrix ECC 200 bar code symbology According to ISO/IEC 16022:2006.
*
* <p>
* Data Matrix is a 2D matrix symbology capable of encoding characters in the ISO/IEC 8859-1
* (Latin-1) character set.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class DataMatrix extends Symbol {
 
public enum ForceMode {
NONE, SQUARE, RECTANGULAR
}
 
private enum Mode {
NULL, DM_ASCII, DM_C40, DM_TEXT, DM_X12, DM_EDIFACT, DM_BASE256
}
 
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[] INT_SYMBOL = { 0, 1, 3, 5, 7, 8, 10, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 2, 4, 6, 9, 11, 14 };
 
private static final int[] MATRIX_H = { 10, 12, 8, 14, 8, 16, 12, 18, 20, 12, 22, 16, 24, 26, 16, 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144 };
 
private static final int[] MATRIX_W = { 10, 12, 18, 14, 32, 16, 26, 18, 20, 36, 22, 36, 24, 26, 48, 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144 };
 
private static final int[] MATRIX_FH = { 10, 12, 8, 14, 8, 16, 12, 18, 20, 12, 22, 16, 24, 26, 16, 16, 18, 20, 22, 24, 26, 16, 18, 20, 22, 24, 26, 20, 22, 24 };
 
private static final int[] MATRIX_FW = { 10, 12, 18, 14, 16, 16, 26, 18, 20, 18, 22, 18, 24, 26, 24, 16, 18, 20, 22, 24, 26, 16, 18, 20, 22, 24, 26, 20, 22, 24 };
 
private static final int[] MATRIX_BYTES = { 3, 5, 5, 8, 10, 12, 16, 18, 22, 22, 30, 32, 36, 44, 49, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050, 1304, 1558 };
 
private static final int[] MATRIX_DATA_BLOCK = { 3, 5, 5, 8, 10, 12, 16, 18, 22, 22, 30, 32, 36, 44, 49, 62, 86, 114, 144, 174, 102, 140, 92, 114, 144, 174, 136, 175, 163, 156 };
 
private static final int[] MATRIX_RS_BLOCK = { 5, 7, 7, 10, 11, 12, 14, 14, 18, 18, 20, 24, 24, 28, 28, 36, 42, 48, 56, 68, 42, 56, 36, 48, 56, 68, 56, 68, 62, 62 };
 
private static final int DM_SIZES_COUNT = MATRIX_H.length;
 
// user-specified values and settings
 
private ForceMode forceMode = ForceMode.NONE;
private int preferredSize;
private int structuredAppendFileId = 1;
private int structuredAppendPosition = 1;
private int structuredAppendTotal = 1;
 
// internal state calculated when setContent() is called
 
private int actualSize = -1;
private final int[] target = new int[2200];
private final int[] binary = new int[2200];
private int binary_length;
private Mode last_mode;
private int[] places;
private int process_p;
private final int[] process_buffer = new int[8];
private int codewordCount;
 
/**
* Forces the symbol to be either square or rectangular (non-square).
*
* @param forceMode the force mode to use
*/
public void setForceMode(final ForceMode forceMode) {
this.forceMode = forceMode;
}
 
/**
* Returns the force mode used by this symbol.
*
* @return the force mode used by this symbol
*/
public ForceMode getForceMode() {
return this.forceMode;
}
 
/**
* Sets the preferred symbol size according to the values in the following table. Values may be
* ignored if the data is too big to fit in the specified symbol, or if
* {@link #setForceMode(ForceMode)} has been invoked.
*
* <table summary="Available Data Matrix symbol sizes">
* <tbody>
* <tr>
* <th>Input</th>
* <th>Symbol Size</th>
* <th>Input</th>
* <th>Symbol Size</th>
* </tr>
* <tr>
* <td>1</td>
* <td>10 x 10</td>
* <td>16</td>
* <td>64 x 64</td>
* </tr>
* <tr>
* <td>2</td>
* <td>12 x 12</td>
* <td>17</td>
* <td>72 x 72</td>
* </tr>
* <tr>
* <td>3</td>
* <td>14 x 14</td>
* <td>18</td>
* <td>80 x 80</td>
* </tr>
* <tr>
* <td>4</td>
* <td>16 x 16</td>
* <td>19</td>
* <td>88 x 88</td>
* </tr>
* <tr>
* <td>5</td>
* <td>18 x 18</td>
* <td>20</td>
* <td>96 x 96</td>
* </tr>
* <tr>
* <td>6</td>
* <td>20 x 20</td>
* <td>21</td>
* <td>104 x 104</td>
* </tr>
* <tr>
* <td>7</td>
* <td>22 x 22</td>
* <td>22</td>
* <td>120 x 120</td>
* </tr>
* <tr>
* <td>8</td>
* <td>24 x 24</td>
* <td>23</td>
* <td>132 x 132</td>
* </tr>
* <tr>
* <td>9</td>
* <td>26 x 26</td>
* <td>24</td>
* <td>144 x 144</td>
* </tr>
* <tr>
* <td>10</td>
* <td>32 x 32</td>
* <td>25</td>
* <td>8 x 18</td>
* </tr>
* <tr>
* <td>11</td>
* <td>36 x 36</td>
* <td>26</td>
* <td>8 x 32</td>
* </tr>
* <tr>
* <td>12</td>
* <td>40 x 40</td>
* <td>27</td>
* <td>12 x 26</td>
* </tr>
* <tr>
* <td>13</td>
* <td>44 x 44</td>
* <td>28</td>
* <td>12 x 36</td>
* </tr>
* <tr>
* <td>14</td>
* <td>48 x 48</td>
* <td>29</td>
* <td>16 x 36</td>
* </tr>
* <tr>
* <td>15</td>
* <td>52 x 52</td>
* <td>30</td>
* <td>16 x 48</td>
* </tr>
* </tbody>
* </table>
*
* @param size the symbol size to use (1 - 30 inclusive)
*/
public void setPreferredSize(final int size) {
this.preferredSize = size;
}
 
/**
* Returns the preferred symbol size.
*
* @return the preferred symbol size
* @see #setPreferredSize(int)
*/
public int getPreferredSize() {
return this.preferredSize;
}
 
/**
* Returns the actual symbol size used. Available after the symbol is encoded.
*
* @return the actual symbol size used
*/
public int getActualSize() {
if (this.actualSize != -1) {
return this.actualSize;
} else {
throw new IllegalStateException("Actual size not calculated until symbol is encoded.");
}
}
 
/**
* Returns the actual width (columns) used for the symbol. Available after the symbol is
* encoded.
*
* @return the actual width (columns) used for the symbol
*/
public int getActualWidth() {
final int index1 = getActualSize() - 1;
final int index2 = INT_SYMBOL[index1];
return MATRIX_W[index2];
}
 
/**
* Returns the actual height (rows) used for the symbol. Available after the symbol is encoded.
*
* @return the actual height (rows) used for the symbol
*/
public int getActualHeight() {
final int index1 = getActualSize() - 1;
final int index2 = INT_SYMBOL[index1];
return MATRIX_H[index2];
}
 
/**
* If this Data Matrix symbol is part of a series of Data Matrix symbols appended in a
* structured format, this method sets the position of this symbol in the series. Valid values
* are 1 through 16 inclusive.
*
* @param position the position of this Data Matrix symbol in the structured append series
*/
public void setStructuredAppendPosition(final int position) {
if (position < 1 || position > 16) {
throw new IllegalArgumentException("Invalid Data Matrix structured append position: " + position);
}
this.structuredAppendPosition = position;
}
 
/**
* Returns the position of this Data Matrix 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 Data Matrix symbol in a series of symbols using structured
* append
*/
public int getStructuredAppendPosition() {
return this.structuredAppendPosition;
}
 
/**
* If this Data Matrix symbol is part of a series of Data Matrix symbols appended in a
* structured format, this method sets the total number of symbols in the series. Valid values
* are 1 through 16 inclusive. A value of 1 indicates that this symbol is not part of a
* structured append series.
*
* @param total the total number of Data Matrix symbols in the structured append series
*/
public void setStructuredAppendTotal(final int total) {
if (total < 1 || total > 16) {
throw new IllegalArgumentException("Invalid Data Matrix structured append total: " + total);
}
this.structuredAppendTotal = total;
}
 
/**
* Returns the size of the series of Data Matrix 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 Data Matrix symbol is part of a series of Data Matrix symbols appended in a
* structured format, this method sets the unique file ID for the series. Valid values are 1
* through 64,516 inclusive.
*
* @param fileId the unique file ID for the series that this symbol is part of
*/
public void setStructuredAppendFileId(final int fileId) {
if (fileId < 1 || fileId > 64_516) {
throw new IllegalArgumentException("Invalid Data Matrix structured append file ID: " + fileId);
}
this.structuredAppendFileId = fileId;
}
 
/**
* Returns the unique file ID of the series of Data Matrix 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 the unique file ID for the series that this symbol is part of
*/
public int getStructuredAppendFileId() {
return this.structuredAppendFileId;
}
 
@Override
protected boolean gs1Supported() {
return true;
}
 
@Override
protected void encode() {
 
int i, binlen, skew = 0;
int symbolsize, optionsize, calcsize;
int taillength;
int H, W, FH, FW, datablock, bytes, rsblock;
int x, y, NC, NR, v;
int[] grid;
final StringBuilder bin = new StringBuilder();
 
eciProcess(); // Get ECI mode
 
binlen = generateCodewords();
 
if (this.preferredSize >= 1 && this.preferredSize <= DM_SIZES_COUNT) {
optionsize = INT_SYMBOL[this.preferredSize - 1];
} else {
optionsize = -1;
}
 
calcsize = DM_SIZES_COUNT - 1;
for (i = DM_SIZES_COUNT - 1; i > -1; i--) {
if (MATRIX_BYTES[i] >= binlen + this.process_p) {
calcsize = i;
}
}
 
if (optionsize == -1) {
// We are in automatic size mode as the exact symbol size was not given
// Now check the detailed search options square only or rectangular only
if (this.forceMode == ForceMode.SQUARE) {
/* Skip rectangular symbols in square only mode */
while (calcsize < DM_SIZES_COUNT && MATRIX_H[calcsize] != MATRIX_W[calcsize]) {
calcsize++;
}
} else if (this.forceMode == ForceMode.RECTANGULAR) {
/* Skip square symbols in rectangular only mode */
while (calcsize < DM_SIZES_COUNT && MATRIX_H[calcsize] == MATRIX_W[calcsize]) {
calcsize++;
}
}
if (calcsize >= DM_SIZES_COUNT) {
throw new OkapiException("Data too long to fit in symbol");
}
symbolsize = calcsize;
} else {
// The symbol size was specified by the user
// Thus check if the data fits into this symbol size and use this size
if (calcsize > optionsize) {
throw new OkapiException("Input too long for selected symbol size");
}
symbolsize = optionsize;
}
 
// Now we know the symbol size we can handle the remaining data in the process buffer.
final int symbolsLeft = MATRIX_BYTES[symbolsize] - binlen;
binlen = encodeRemainder(symbolsLeft, binlen);
 
if (binlen > MATRIX_BYTES[symbolsize]) {
throw new OkapiException("Data too long to fit in symbol");
}
 
H = MATRIX_H[symbolsize];
W = MATRIX_W[symbolsize];
FH = MATRIX_FH[symbolsize];
FW = MATRIX_FW[symbolsize];
bytes = MATRIX_BYTES[symbolsize];
datablock = MATRIX_DATA_BLOCK[symbolsize];
rsblock = MATRIX_RS_BLOCK[symbolsize];
 
this.codewordCount = datablock + rsblock; // data codewords + error correction codewords
 
taillength = bytes - binlen;
 
if (taillength != 0) {
addPadBits(binlen, taillength);
}
 
// ecc code
if (symbolsize == 29) {
skew = 1;
}
calculateErrorCorrection(bytes, datablock, rsblock, skew);
NC = W - 2 * (W / FW);
NR = H - 2 * (H / FH);
this.places = new int[NC * NR];
placeData(NR, NC);
grid = new int[W * H];
for (i = 0; i < W * H; i++) {
grid[i] = 0;
}
for (y = 0; y < H; y += FH) {
for (x = 0; x < W; x++) {
grid[y * W + x] = 1;
}
for (x = 0; x < W; x += 2) {
grid[(y + FH - 1) * W + x] = 1;
}
}
for (x = 0; x < W; x += FW) {
for (y = 0; y < H; y++) {
grid[y * W + x] = 1;
}
for (y = 0; y < H; y += 2) {
grid[y * W + x + FW - 1] = 1;
}
}
for (y = 0; y < NR; y++) {
for (x = 0; x < NC; x++) {
v = this.places[(NR - y - 1) * NC + x];
if (v == 1 || v > 7 && (this.target[(v >> 3) - 1] & 1 << (v & 7)) != 0) {
grid[(1 + y + 2 * (y / (FH - 2))) * W + 1 + x + 2 * (x / (FW - 2))] = 1;
}
}
}
 
this.actualSize = positionOf(symbolsize, INT_SYMBOL) + 1;
this.readable = "";
this.pattern = new String[H];
this.row_count = H;
this.row_height = new int[H];
for (y = H - 1; y >= 0; y--) {
bin.setLength(0);
for (x = 0; x < W; x++) {
if (grid[W * y + x] == 1) {
bin.append('1');
} else {
bin.append('0');
}
}
this.pattern[H - y - 1] = bin2pat(bin);
this.row_height[H - y - 1] = this.moduleWidth;
}
 
infoLine("Grid Size: " + W + " X " + H);
infoLine("Data Codewords: " + datablock);
infoLine("ECC Codewords: " + rsblock);
}
 
@Override
protected int[] getCodewords() {
return Arrays.copyOf(this.target, this.codewordCount);
}
 
private int generateCodewords() {
/* Encodes data using ASCII, C40, Text, X12, EDIFACT or Base 256 modes as appropriate */
/* Supports encoding FNC1 in supporting systems */
/* Supports ECI encoding for whole message only, not inline switching */
 
info("Encoding: ");
int sp, tp, i;
Mode current_mode, next_mode;
int inputlen = this.inputData.length;
 
sp = 0;
tp = 0;
this.process_p = 0;
 
for (i = 0; i < 8; i++) {
this.process_buffer[i] = 0;
}
this.binary_length = 0;
 
/* step (a) */
current_mode = Mode.DM_ASCII;
next_mode = Mode.DM_ASCII;
 
if (this.structuredAppendTotal != 1) {
 
/* FNC2 */
this.target[tp] = 233;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("FNC2 ");
 
/* symbol sequence indicator (position + total) */
final int ssi = this.structuredAppendPosition - 1 << 4 | 17 - this.structuredAppendTotal;
this.target[tp] = ssi;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
infoSpace(ssi);
 
/* file identification codeword 1 (valid values 1 - 254) */
final int id1 = 1 + (this.structuredAppendFileId - 1) / 254;
this.target[tp] = id1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
infoSpace(id1);
 
/* file identification codeword 2 (valid values 1 - 254) */
final int id2 = 1 + (this.structuredAppendFileId - 1) % 254;
this.target[tp] = id2;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
infoSpace(id2);
}
 
if (this.inputDataType == DataType.GS1) {
this.target[tp] = 232;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("FNC1 ");
} /* FNC1 */
 
if (this.readerInit) {
if (this.inputDataType == DataType.GS1) {
throw new OkapiException("Cannot encode in GS1 mode and Reader Initialisation at the same time");
} else {
this.target[tp] = 234; /* FNC3 */
tp++; /* Reader Programming */
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("RP ");
}
}
 
if (this.eciMode != 3) {
this.target[tp] = 241; // ECI
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
if (this.eciMode <= 126) {
this.target[tp] = this.eciMode + 1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
}
if (this.eciMode >= 127 && this.eciMode <= 16382) {
this.target[tp] = (this.eciMode - 127) / 254 + 128;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.target[tp] = (this.eciMode - 127) % 254 + 1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
}
if (this.eciMode >= 16383) {
this.target[tp] = (this.eciMode - 16383) / 64516 + 192;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.target[tp] = (this.eciMode - 16383) / 254 % 254 + 1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.target[tp] = (this.eciMode - 16383) % 254 + 1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
}
info("ECI " + this.eciMode + " ");
}
 
/* Check for Macro05/Macro06 */
/* "[)>[RS]05[GS]...[RS][EOT]" -> CW 236 */
/* "[)>[RS]06[GS]...[RS][EOT]" -> CW 237 */
if (tp == 0 & sp == 0 && inputlen >= 9) {
if (this.inputData[0] == '[' && this.inputData[1] == ')' && this.inputData[2] == '>' && this.inputData[3] == '\u001e' && this.inputData[4] == '0'
&& (this.inputData[5] == '5' || this.inputData[5] == '6') && this.inputData[6] == '\u001d' && this.inputData[inputlen - 2] == '\u001e'
&& this.inputData[inputlen - 1] == '\u0004') {
/* Output macro codeword */
if (this.inputData[5] == '5') {
this.target[tp] = 236;
info("Macro05 ");
} else {
this.target[tp] = 237;
info("Macro06 ");
}
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
/* Remove macro characters from input string */
sp = 7;
inputlen -= 2;
this.inputData = Arrays.copyOf(this.inputData, this.inputData.length - 2);
}
}
 
while (sp < inputlen) {
 
current_mode = next_mode;
 
/* step (b) - ASCII encodation */
if (current_mode == Mode.DM_ASCII) {
next_mode = Mode.DM_ASCII;
 
for (i = 0; i < 8; i++) {
this.process_buffer[i] = 0;
}
 
if (isTwoDigits(sp)) {
this.target[tp] = 10 * Character.getNumericValue(this.inputData[sp]) + Character.getNumericValue(this.inputData[sp + 1]) + 130;
infoSpace(this.target[tp] - 130);
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
sp += 2;
} else {
next_mode = lookAheadTest(sp, current_mode);
 
if (next_mode != Mode.DM_ASCII) {
switch (next_mode) {
case DM_C40:
this.target[tp] = 230;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("C40 ");
break;
case DM_TEXT:
this.target[tp] = 239;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("TEX ");
break;
case DM_X12:
this.target[tp] = 238;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("X12 ");
break;
case DM_EDIFACT:
this.target[tp] = 240;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("EDI ");
break;
case DM_BASE256:
this.target[tp] = 231;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("BAS ");
break;
}
} else {
if (this.inputData[sp] > 127) {
this.target[tp] = 235; /* FNC4 */
 
info("FNC4 ");
tp++;
this.target[tp] = this.inputData[sp] - 128 + 1;
infoSpace(this.target[tp] - 1);
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
} else {
if (this.inputData[sp] == FNC1) {
this.target[tp] = 232; /* FNC1 */
info("FNC1 ");
} else {
this.target[tp] = this.inputData[sp] + 1;
infoSpace(this.target[tp] - 1);
}
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
}
sp++;
}
}
}
 
/* step (c) C40 encodation */
if (current_mode == Mode.DM_C40) {
int shift_set, value;
 
next_mode = Mode.DM_C40;
if (this.process_p == 0) {
next_mode = lookAheadTest(sp, current_mode);
}
 
if (next_mode != Mode.DM_C40) {
this.target[tp] = 254;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++; /* Unlatch */
 
next_mode = Mode.DM_ASCII;
info("ASC ");
} else {
if (this.inputData[sp] == FNC1) {
shift_set = 2;
value = 27; /* FNC1 */
} else if (this.inputData[sp] > 127) {
this.process_buffer[this.process_p] = 1;
this.process_p++;
this.process_buffer[this.process_p] = 30;
this.process_p++; /* Upper Shift */
 
shift_set = C40_SHIFT[this.inputData[sp] - 128];
value = C40_VALUE[this.inputData[sp] - 128];
} else {
shift_set = C40_SHIFT[this.inputData[sp]];
value = C40_VALUE[this.inputData[sp]];
}
 
if (shift_set != 0) {
this.process_buffer[this.process_p] = shift_set - 1;
this.process_p++;
}
this.process_buffer[this.process_p] = value;
this.process_p++;
 
while (this.process_p >= 3) {
int iv;
 
iv = 1600 * this.process_buffer[0] + 40 * this.process_buffer[1] + this.process_buffer[2] + 1;
this.target[tp] = iv / 256;
tp++;
this.target[tp] = iv % 256;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("(" + this.process_buffer[0] + " " + this.process_buffer[1] + " " + this.process_buffer[2] + ") ");
 
this.process_buffer[0] = this.process_buffer[3];
this.process_buffer[1] = this.process_buffer[4];
this.process_buffer[2] = this.process_buffer[5];
this.process_buffer[3] = 0;
this.process_buffer[4] = 0;
this.process_buffer[5] = 0;
this.process_p -= 3;
}
sp++;
}
}
 
/* step (d) Text encodation */
if (current_mode == Mode.DM_TEXT) {
int shift_set, value;
 
next_mode = Mode.DM_TEXT;
if (this.process_p == 0) {
next_mode = lookAheadTest(sp, current_mode);
}
 
if (next_mode != Mode.DM_TEXT) {
this.target[tp] = 254;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++; /* Unlatch */
 
next_mode = Mode.DM_ASCII;
info("ASC ");
} else {
if (this.inputData[sp] == FNC1) {
shift_set = 2;
value = 27; /* FNC1 */
} else if (this.inputData[sp] > 127) {
this.process_buffer[this.process_p] = 1;
this.process_p++;
this.process_buffer[this.process_p] = 30;
this.process_p++; /* Upper Shift */
 
shift_set = TEXT_SHIFT[this.inputData[sp] - 128];
value = TEXT_VALUE[this.inputData[sp] - 128];
} else {
shift_set = TEXT_SHIFT[this.inputData[sp]];
value = TEXT_VALUE[this.inputData[sp]];
}
 
if (shift_set != 0) {
this.process_buffer[this.process_p] = shift_set - 1;
this.process_p++;
}
this.process_buffer[this.process_p] = value;
this.process_p++;
 
while (this.process_p >= 3) {
int iv;
 
iv = 1600 * this.process_buffer[0] + 40 * this.process_buffer[1] + this.process_buffer[2] + 1;
this.target[tp] = iv / 256;
tp++;
this.target[tp] = iv % 256;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("(" + this.process_buffer[0] + " " + this.process_buffer[1] + " " + this.process_buffer[2] + ") ");
 
this.process_buffer[0] = this.process_buffer[3];
this.process_buffer[1] = this.process_buffer[4];
this.process_buffer[2] = this.process_buffer[5];
this.process_buffer[3] = 0;
this.process_buffer[4] = 0;
this.process_buffer[5] = 0;
this.process_p -= 3;
}
sp++;
}
}
 
/* step (e) X12 encodation */
if (current_mode == Mode.DM_X12) {
int value = 0;
 
next_mode = Mode.DM_X12;
if (this.process_p == 0) {
next_mode = lookAheadTest(sp, current_mode);
}
 
if (next_mode != Mode.DM_X12) {
this.target[tp] = 254;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++; /* Unlatch */
 
next_mode = Mode.DM_ASCII;
info("ASC ");
} else {
if (this.inputData[sp] == 13) {
value = 0;
}
if (this.inputData[sp] == '*') {
value = 1;
}
if (this.inputData[sp] == '>') {
value = 2;
}
if (this.inputData[sp] == ' ') {
value = 3;
}
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') {
value = this.inputData[sp] - '0' + 4;
}
if (this.inputData[sp] >= 'A' && this.inputData[sp] <= 'Z') {
value = this.inputData[sp] - 'A' + 14;
}
 
this.process_buffer[this.process_p] = value;
this.process_p++;
 
while (this.process_p >= 3) {
int iv;
 
iv = 1600 * this.process_buffer[0] + 40 * this.process_buffer[1] + this.process_buffer[2] + 1;
this.target[tp] = iv / 256;
tp++;
this.target[tp] = iv % 256;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("(" + this.process_buffer[0] + " " + this.process_buffer[1] + " " + this.process_buffer[2] + ") ");
 
this.process_buffer[0] = this.process_buffer[3];
this.process_buffer[1] = this.process_buffer[4];
this.process_buffer[2] = this.process_buffer[5];
this.process_buffer[3] = 0;
this.process_buffer[4] = 0;
this.process_buffer[5] = 0;
this.process_p -= 3;
}
sp++;
}
}
 
/* step (f) EDIFACT encodation */
if (current_mode == Mode.DM_EDIFACT) {
int value = 0;
 
next_mode = Mode.DM_EDIFACT;
if (this.process_p == 3) {
next_mode = lookAheadTest(sp, current_mode);
}
 
if (next_mode != Mode.DM_EDIFACT) {
this.process_buffer[this.process_p] = 31;
this.process_p++;
next_mode = Mode.DM_ASCII;
} else {
if (this.inputData[sp] >= '@' && this.inputData[sp] <= '^') {
value = this.inputData[sp] - '@';
}
if (this.inputData[sp] >= ' ' && this.inputData[sp] <= '?') {
value = this.inputData[sp];
}
 
this.process_buffer[this.process_p] = value;
this.process_p++;
sp++;
}
 
while (this.process_p >= 4) {
this.target[tp] = (this.process_buffer[0] << 2) + ((this.process_buffer[1] & 0x30) >> 4);
tp++;
this.target[tp] = ((this.process_buffer[1] & 0x0f) << 4) + ((this.process_buffer[2] & 0x3c) >> 2);
tp++;
this.target[tp] = ((this.process_buffer[2] & 0x03) << 6) + this.process_buffer[3];
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("(" + this.process_buffer[0] + " " + this.process_buffer[1] + " " + this.process_buffer[2] + ") ");
 
this.process_buffer[0] = this.process_buffer[4];
this.process_buffer[1] = this.process_buffer[5];
this.process_buffer[2] = this.process_buffer[6];
this.process_buffer[3] = this.process_buffer[7];
this.process_buffer[4] = 0;
this.process_buffer[5] = 0;
this.process_buffer[6] = 0;
this.process_buffer[7] = 0;
this.process_p -= 4;
}
}
 
/* step (g) Base 256 encodation */
if (current_mode == Mode.DM_BASE256) {
next_mode = lookAheadTest(sp, current_mode);
 
if (next_mode == Mode.DM_BASE256) {
this.target[tp] = this.inputData[sp];
infoSpace(this.target[tp]);
tp++;
sp++;
this.binary[this.binary_length] = 'b';
this.binary_length++;
} else {
next_mode = Mode.DM_ASCII;
info("ASC ");
}
}
 
if (tp > 1558) {
throw new OkapiException("Data too long to fit in symbol");
}
 
} /* while */
 
/* Add length and randomising algorithm to b256 */
i = 0;
while (i < tp) {
if (this.binary[i] == 'b') {
if (i == 0 || this.binary[i - 1] != 'b') {
/* start of binary data */
int binary_count; /* length of b256 data */
 
for (binary_count = 0; binary_count + i < tp && this.binary[binary_count + i] == 'b'; binary_count++) {
;
}
 
if (binary_count <= 249) {
insertAt(i, 'b');
insertValueAt(i, tp, (char) binary_count);
tp++;
} else {
insertAt(i, 'b');
insertAt(i + 1, 'b');
insertValueAt(i, tp, (char) (binary_count / 250 + 249));
tp++;
insertValueAt(i + 1, tp, (char) (binary_count % 250));
tp++;
}
}
}
i++;
}
 
for (i = 0; i < tp; i++) {
if (this.binary[i] == 'b') {
int prn, temp;
 
prn = 149 * (i + 1) % 255 + 1;
temp = this.target[i] + prn;
if (temp <= 255) {
this.target[i] = temp;
} else {
this.target[i] = temp - 256;
}
}
}
 
infoLine();
info("Codewords: ");
for (i = 0; i < tp; i++) {
infoSpace(this.target[i]);
}
infoLine();
 
this.last_mode = current_mode;
return tp;
}
 
private int encodeRemainder(final int symbols_left, int target_length) {
 
final int inputlen = this.inputData.length;
 
switch (this.last_mode) {
case DM_C40:
case DM_TEXT:
if (this.process_p == 1) // 1 data character left to encode.
{
if (symbols_left > 1) {
this.target[target_length] = 254;
target_length++; // Unlatch and encode remaining data in ascii.
}
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
} else if (this.process_p == 2) // 2 data characters left to encode.
{
// Pad with shift 1 value (0) and encode as double.
final int intValue = 1600 * this.process_buffer[0] + 40 * this.process_buffer[1] + 1; // ie
// (0
// +
// 1).
this.target[target_length] = intValue / 256;
target_length++;
this.target[target_length] = intValue % 256;
target_length++;
if (symbols_left > 2) {
this.target[target_length] = 254; // Unlatch
target_length++;
}
} else {
if (symbols_left > 0) {
this.target[target_length] = 254; // Unlatch
target_length++;
}
}
break;
 
case DM_X12:
if (symbols_left == this.process_p && this.process_p == 1) {
// Unlatch not required!
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
} else {
this.target[target_length] = 254;
target_length++; // Unlatch.
 
if (this.process_p == 1) {
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
}
 
if (this.process_p == 2) {
this.target[target_length] = this.inputData[inputlen - 2] + 1;
target_length++;
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
}
}
break;
 
case DM_EDIFACT:
if (symbols_left <= 2) // Unlatch not required!
{
if (this.process_p == 1) {
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
}
 
if (this.process_p == 2) {
this.target[target_length] = this.inputData[inputlen - 2] + 1;
target_length++;
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
}
} else {
// Append edifact unlatch value (31) and empty buffer
if (this.process_p == 0) {
this.target[target_length] = 31 << 2;
target_length++;
}
 
if (this.process_p == 1) {
this.target[target_length] = (this.process_buffer[0] << 2) + ((31 & 0x30) >> 4);
target_length++;
this.target[target_length] = (31 & 0x0f) << 4;
target_length++;
}
 
if (this.process_p == 2) {
this.target[target_length] = (this.process_buffer[0] << 2) + ((this.process_buffer[1] & 0x30) >> 4);
target_length++;
this.target[target_length] = ((this.process_buffer[1] & 0x0f) << 4) + ((31 & 0x3c) >> 2);
target_length++;
this.target[target_length] = (31 & 0x03) << 6;
target_length++;
}
 
if (this.process_p == 3) {
this.target[target_length] = (this.process_buffer[0] << 2) + ((this.process_buffer[1] & 0x30) >> 4);
target_length++;
this.target[target_length] = ((this.process_buffer[1] & 0x0f) << 4) + ((this.process_buffer[2] & 0x3c) >> 2);
target_length++;
this.target[target_length] = ((this.process_buffer[2] & 0x03) << 6) + 31;
target_length++;
}
}
break;
}
 
return target_length;
}
 
private boolean isTwoDigits(final int pos) {
return pos + 1 < this.inputData.length && Character.isDigit((char) this.inputData[pos]) && Character.isDigit((char) this.inputData[pos + 1]);
}
 
private Mode lookAheadTest(final int position, final Mode current_mode) {
 
/* 'look ahead test' from Annex P */
 
double ascii_count, c40_count, text_count, x12_count, edf_count, b256_count, best_count;
int sp;
final int sourcelen = this.inputData.length;
Mode best_scheme = Mode.NULL;
final double stiction = 1.0F / 24.0F; // smallest change to act on, to get around floating
// point inaccuracies
 
/* step (j) */
if (current_mode == Mode.DM_ASCII) {
ascii_count = 0.0;
c40_count = 1.0;
text_count = 1.0;
x12_count = 1.0;
edf_count = 1.0;
b256_count = 1.25;
} else {
ascii_count = 1.0;
c40_count = 2.0;
text_count = 2.0;
x12_count = 2.0;
edf_count = 2.0;
b256_count = 2.25;
}
 
switch (current_mode) {
case DM_C40: // (j)(2)
c40_count = 0.0;
break;
case DM_TEXT: // (j)(3)
text_count = 0.0;
break;
case DM_X12: // (j)(4)
x12_count = 0.0;
break;
case DM_EDIFACT: // (j)(5)
edf_count = 0.0;
break;
case DM_BASE256: // (j)(6)
b256_count = 0.0;
break;
}
 
sp = position;
 
do {
if (sp == sourcelen) {
/* At the end of data ... step (k) */
ascii_count = Math.ceil(ascii_count);
b256_count = Math.ceil(b256_count);
edf_count = Math.ceil(edf_count);
text_count = Math.ceil(text_count);
x12_count = Math.ceil(x12_count);
c40_count = Math.