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 Customer Support/src/org/openconcerto/modules/customersupport/Module.java
1,6 → 1,5
package org.openconcerto.modules.customersupport;
 
import java.io.File;
import java.io.IOException;
import java.sql.Date;
import java.sql.SQLException;
9,7 → 8,6
import java.util.List;
import java.util.Set;
 
import org.openconcerto.erp.config.Gestion;
import org.openconcerto.erp.config.MainFrame;
import org.openconcerto.erp.modules.AbstractModule;
import org.openconcerto.erp.modules.ComponentsContext;
16,20 → 14,15
import org.openconcerto.erp.modules.DBContext;
import org.openconcerto.erp.modules.MenuContext;
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.Configuration;
import org.openconcerto.sql.element.GlobalMapper;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.FieldPath;
import org.openconcerto.sql.model.SQLRequestLog;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.ui.ConnexionPanel;
import org.openconcerto.sql.utils.SQLCreateTable;
import org.openconcerto.sql.view.ListeAddPanel;
import org.openconcerto.sql.view.list.BaseSQLTableModelColumn;
/trunk/Modules/Module Customer Support/src/org/openconcerto/modules/customersupport/labels_fr.xml
1,7 → 1,7
<?xml version="1.0" encoding="UTF-8" ?>
<ROOT>
<element refid="customersupport.ticket" nameClass="masculine" name="ticket de support">
<FIELD name="STATUS" label="Status" />
<FIELD name="STATUS" label="Statut" />
<FIELD name="LABEL" label="Libellé" />
<FIELD name="ID_CLIENT" label="Client" />
<FIELD name="NUMBER" label="Numéro" />
10,7 → 10,7
<FIELD name="RATING" label="Priorité" />
<FIELD name="TYPE" label="Type" />
<FIELD name="REMIND_DATE" label="Prochain rappel le" />
<FIELD name="DATE" label="Date" />
<FIELD name="DATE" label="Date ticket" />
<FIELD name="CLOSED_AND_ARCHIVED" label="Archivé" />
</element>
<element refid="customersupport.ticket.history" nameClass="feminine" name="Intervention sur ticket">
17,6 → 17,6
<FIELD name="ID_CUSTOMER_SUPPORT_TICKET" label="Ticket" />
<FIELD name="ID_USER_COMMON" label="Utilisateur en charge" />
<FIELD name="INFORMATION" label="Détails de l'intervention" />
<FIELD name="DATE" label="Date" />
<FIELD name="DATE" label="Date inter." />
</element>
</ROOT>
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/CalendarPrintPanel.java
43,6 → 43,8
private static final double POINTS_PER_INCH = 72.0;
 
public CalendarPrintPanel(final OperationCalendarManager manager, final int week, final int year, final List<User> selectedUsers, final List<String> selectedStates) {
 
System.err.println("CalendarPrintPanel.CalendarPrintPanel()" + selectedUsers);
preview.setSelected(true);
//
this.setLayout(new GridBagLayout());
109,8 → 111,15
});
final PageFormat pf = new PageFormat();
pf.setPaper(new A4());
final CalendarItemPrinter printable = new OperationCalendarItemPrinter(user.getFullName(), itemInWeek, pf);
List<JCalendarItem> itemsToWork = new ArrayList<>();
for (JCalendarItem item : itemInWeek) {
if (!item.hasFlag(ModuleOperation.FREE_TIME_FLAG)) {
itemsToWork.add(item);
}
}
 
final CalendarItemPrinter printable = new OperationCalendarItemPrinter(user.getFullName(), itemInWeek, pf, itemsToWork);
 
p.add(printable);
}
}
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/JCalendarItemDB.java
25,7 → 25,13
assert r.isFrozen();
this.item = r;
this.group = dir.getElement(this.item.getTable()).getContainer(this.item);
if (this.group == null) {
throw new IllegalArgumentException("no group found for row " + r);
}
this.operationElem = dir.getElement(OperationSQLElement.class);
if (this.operationElem == null) {
throw new IllegalStateException("no element found " + OperationSQLElement.class);
}
this.operation = CollectionUtils.getSole(this.group.getReferentRows(this.operationElem.getTable()));
}
 
84,7 → 90,7
}
 
public String getStatus() {
return status;
return this.status;
}
 
public void setOperationType(String type) {
92,7 → 98,7
}
 
public String getType() {
return type;
return this.type;
}
 
public String getFlagsString() {
109,7 → 115,7
}
 
public String getPlannerXML() {
return plannerXML;
return this.plannerXML;
}
 
public void setPlannerXML(String string) {
117,7 → 123,7
}
 
public String getPlannerUID() {
return plannerUID;
return this.plannerUID;
}
 
public void setPlannerUID(String plannerUID) {
125,7 → 131,7
}
 
public String getSiteName() {
return siteName;
return this.siteName;
}
 
public void setSiteName(String siteName) {
133,7 → 139,7
}
 
public String getSiteComment() {
return siteComment;
return this.siteComment;
}
 
public void setSiteComment(String siteComment) {
145,7 → 151,7
}
 
public Number getSiteId() {
return siteId;
return this.siteId;
}
 
public int getId() {
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationCalendarPanel.java
117,12 → 117,11
this.beginStateSaving(conf.getConfDir(), w);
}
 
public static Map<Integer, Long> getDurations(List<List<JCalendarItem>> list, final Flag requiredFlag) {
public static Map<Integer, Long> getDurations(List<List<JCalendarItem>> list, final Flag requiredFlag, final Flag excludedFlag) {
final Map<Integer, Long> res = new HashMap<>();
final Flag freeTimeFlag = ModuleOperation.FREE_TIME_FLAG;
for (List<JCalendarItem> items : list) {
for (JCalendarItem item : items) {
if (!item.hasFlag(freeTimeFlag) && (requiredFlag == null || item.hasFlag(requiredFlag)) && item.getCookie() instanceof SQLRowValues) {
if (!item.hasFlag(excludedFlag) && (requiredFlag == null || item.hasFlag(requiredFlag)) && item.getCookie() instanceof SQLRowValues) {
final SQLRowValues user = (SQLRowValues) item.getCookie();
final long toAddMinutes = (item.getDtEnd().getTimeInMillis() - item.getDtStart().getTimeInMillis()) / (60 * 1000);
final Integer key = user.getID();
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/UserOperationListModel.java
140,7 → 140,7
return this.usersAndWeeklyMinutes;
}
}
final Map<User, Integer> uInfo = new LinkedHashMap<User, Integer>();
final Map<User, Integer> uInfo = new LinkedHashMap<>();
final SQLRowValues v = new SQLRowValues(this.salarieElem.getTable());
v.putNulls("NOM", "PRENOM");
v.putRowValues("ID_INFOS_SALARIE_PAYE").putNulls("DUREE_HEBDO");
152,10 → 152,14
for (int i = 0; i < size; i++) {
final User u = users.get(i);
final String name = u.getName().trim();
final String firstName = u.getFirstName();
final String firstName = u.getFirstName().trim();
Integer minutes = null;
for (SQLRowValues row : rows) {
if (row.getString("NOM").trim().equalsIgnoreCase(name) && row.getString("PRENOM").trim().equalsIgnoreCase(firstName)) {
// Matching Utilisateur <-> Salarié
// Nom et prénom identique
final String sName = row.getString("NOM").trim();
final String sFirstName = row.getString("PRENOM").trim();
if (sName.equalsIgnoreCase(name) && sFirstName.equalsIgnoreCase(firstName)) {
minutes = (int) row.getForeign("ID_INFOS_SALARIE_PAYE").getFloat("DUREE_HEBDO") * 60;
break;
}
169,8 → 173,8
}
 
private void setDurations(final List<List<JCalendarItem>> viewItems) {
final Map<Integer, Long> all = OperationCalendarPanel.getDurations(viewItems, null);
final Map<Integer, Long> locked = OperationCalendarPanel.getDurations(viewItems, Flag.getFlag("locked"));
final Map<Integer, Long> all = OperationCalendarPanel.getDurations(viewItems, null, ModuleOperation.FREE_TIME_FLAG);
final Map<Integer, Long> locked = OperationCalendarPanel.getDurations(viewItems, Flag.getFlag("locked"), ModuleOperation.FREE_TIME_FLAG);
synchronized (this) {
this.allDurations = Collections.unmodifiableMap(all);
this.lockedDurations = Collections.unmodifiableMap(locked);
217,10 → 221,10
// not a SALARIE
suffix = "";
} else {
// Durée verrouillée
final int d2 = getDuration(locked, u.getId());
// Durée planifiée
final int d = getDuration(all, u.getId());
// Durée verrouillée
final int d2 = getDuration(locked, u.getId());
suffix = " [" + formatDuration(d2) + " / " + formatDuration(d) + " / " + formatDuration(weeklyMinutes) + "]";
}
res.add(createItem(u, (u.getFullName() + suffix).trim()));
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationExportPanel.java
74,13 → 74,13
public class OperationExportPanel extends JPanel {
 
@GuardedBy("EDT")
static private final DateFormat DF = new SimpleDateFormat("yyyyMMdd");
private static final DateFormat DF = new SimpleDateFormat("yyyyMMdd");
 
final JCheckBox lockedCheckBox = new JCheckBox("verrouillées uniquement");
final JButton bPrint = new JButton("Exporter");
 
public OperationExportPanel(final OperationCalendarManager manager, final List<SQLRowValues> rowsSite) {
lockedCheckBox.setSelected(true);
this.lockedCheckBox.setSelected(true);
//
this.setLayout(new GridBagLayout());
final GridBagConstraints c = new DefaultGridBagConstraints();
141,8 → 141,8
//
final JPanel p = new JPanel();
p.setLayout(new FlowLayout(FlowLayout.RIGHT));
p.add(lockedCheckBox);
p.add(bPrint);
p.add(this.lockedCheckBox);
p.add(this.bPrint);
c.gridwidth = 2;
c.gridx = 0;
c.gridy++;
151,7 → 151,7
this.add(p, c);
//
 
bPrint.addActionListener(new ActionListener() {
this.bPrint.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
160,9 → 160,9
}
final String statusVal = statusCombo.getValue();
final List<String> states = StringUtils.isEmpty(statusVal, true) ? null : Collections.singletonList(statusVal);
final List<JCalendarItem> items = manager.getItemIn(d1.getDate(), d2.getDate(), null, states);
final List<JCalendarItemDB> itemsToExport = new ArrayList<JCalendarItemDB>(items.size());
if (lockedCheckBox.isSelected()) {
final List<JCalendarItem> items = manager.getItemIn(d1.getDate(), d2.getDate(), manager.getAllUsers(), states);
final List<JCalendarItemDB> itemsToExport = new ArrayList<>(items.size());
if (OperationExportPanel.this.lockedCheckBox.isSelected()) {
for (JCalendarItem jCalendarItem : items) {
JCalendarItemDB i = (JCalendarItemDB) jCalendarItem;
if (i.getFlagsString().contains("locked")) {
176,12 → 176,12
}
}
if (rowsSite != null && !rowsSite.isEmpty()) {
final Set<String> allowedSites = new HashSet<String>();
final Set<String> allowedSites = new HashSet<>();
for (SQLRowValues r : rowsSite) {
String siteName = r.getString("NAME");
allowedSites.add(siteName);
}
final List<JCalendarItemDB> filtered = new ArrayList<JCalendarItemDB>(itemsToExport.size());
final List<JCalendarItemDB> filtered = new ArrayList<>(itemsToExport.size());
for (JCalendarItemDB i : itemsToExport) {
if (allowedSites.contains(i.getSiteName())) {
filtered.add(i);
218,9 → 218,9
});
}
 
static private final class Planner implements Comparable<Planner> {
private static final class Planner implements Comparable<Planner> {
 
static private final BigDecimal MS_PER_HOUR = BigDecimal.valueOf(1000 * 3600);
private static final BigDecimal MS_PER_HOUR = BigDecimal.valueOf(1000 * 3600);
 
private final String uid;
private final String xml;
256,7 → 256,7
 
final Element scheduleElem = doc.getRootElement().getChild("schedule");
this.startTime = new Date(Long.valueOf(scheduleElem.getAttributeValue("start")));
final long endTime = Long.valueOf(scheduleElem.getAttributeValue("end"));
final long endTime = Long.parseLong(scheduleElem.getAttributeValue("end"));
this.hours = DecimalUtils.round(BigDecimal.valueOf(endTime - this.startTime.getTime()).divide(MS_PER_HOUR, DecimalUtils.HIGH_PRECISION), 5);
} catch (Exception e) {
throw new IllegalStateException("couldn't get start for " + this.xml, e);
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationCalendarManager.java
62,6 → 62,10
this.userMngr = userMngr;
}
 
public UserManager getUserMngr() {
return userMngr;
}
 
public final SQLElementDirectory getDirectory() {
return this.dir;
}
389,4 → 393,15
}
return l.get(0);
}
 
/**
* Enabled or disabled users
*/
public List<User> getAllUsers() {
final List<User> result = new ArrayList<>();
for (User user : this.userMngr.getUsers().values()) {
result.add(user);
}
return result;
}
}
/trunk/Modules/Module Operation/src/org/openconcerto/modules/operation/OperationHistoryPanel.java
72,6 → 72,7
public void propertyChange(PropertyChangeEvent evt) {
final List<SQLRowValues> selectedRows = list.getSelectedRows();
final IListPanel listePanel = listHistoriquePanel.getListePanel(0);
// Activation/Desactivation des boutons Mofifier/Supprimer
if (selectedRows != null && !selectedRows.isEmpty()) {
final Set<Long> idsCalendarItemGroup = new HashSet<>();
for (SQLRowValues sqlRowValues : selectedRows) {
142,24 → 143,25
cal.set(Calendar.YEAR, selectedYear + 1);
Date dEnd = cal.getTime();
 
final SQLTable groupT = comboRequest.getPrimaryTable().getTable("CALENDAR_ITEM_GROUP");
final SQLTable calItemT = comboRequest.getPrimaryTable().getTable("CALENDAR_ITEM");
final SQLTable itemGroupTable = comboRequest.getPrimaryTable().getTable("CALENDAR_ITEM_GROUP");
final SQLTable itemTable = comboRequest.getPrimaryTable().getTable("CALENDAR_ITEM");
final SQLTable operationTable = comboRequest.getPrimaryTable().getTable("OPERATION");
final List<?> dateGroupIDs;
{
final SQLSelect copy = new SQLSelect(input);
copy.clearSelect();
copy.addSelect(copy.getAlias(groupT.getKey()));
copy.setWhere(copy.getAlias(comboRequest.getPrimaryTable().getTable("OPERATION").getField("ID_SITE")), "=", panel.getSelectedRow().getID());
final List<?> allGroupIDs = calItemT.getDBSystemRoot().getDataSource().executeCol(copy.asString());
 
copy.addSelect(copy.getAlias(itemGroupTable.getKey()));
copy.setWhere(copy.getAlias(operationTable.getField("ID_SITE")), "=", panel.getSelectedRow().getID());
final List<?> allGroupIDs = itemTable.getDBSystemRoot().getDataSource().executeCol(copy.asString());
final SQLSelect selIDGroup = new SQLSelect();
selIDGroup.addSelect(calItemT.getField("ID_CALENDAR_ITEM_GROUP"));
final Where where = new Where(calItemT.getField("START"), dStart, true, dEnd, true);
selIDGroup.setWhere(where).andWhere(new Where(calItemT.getField("ID_CALENDAR_ITEM_GROUP"), allGroupIDs));
dateGroupIDs = calItemT.getDBSystemRoot().getDataSource().executeCol(selIDGroup.asString());
selIDGroup.addSelect(itemTable.getField("ID_CALENDAR_ITEM_GROUP"));
final Where where = new Where(itemTable.getField("START"), dStart, true, dEnd, true);
selIDGroup.setWhere(where).andWhere(new Where(itemTable.getField("ID_CALENDAR_ITEM_GROUP"), allGroupIDs));
dateGroupIDs = itemTable.getDBSystemRoot().getDataSource().executeCol(selIDGroup.asString());
}
 
input.setWhere(new Where(input.getAlias(groupT.getKey()), dateGroupIDs));
Where w = new Where(input.getAlias(itemGroupTable.getKey()), dateGroupIDs);
input.setWhere(w);
} catch (Throwable e) {
e.printStackTrace();
}
/trunk/Modules/Module 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/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/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/call/CustomerCallSQLElement.java
15,6 → 15,7
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.ui.JDate;
import org.openconcerto.ui.JLabelBold;
import org.openconcerto.ui.component.ITextArea;
 
public class CustomerCallSQLElement extends ModuleElement {
 
27,6 → 28,11
final List<String> l = new ArrayList<String>();
l.add("DATE");
l.add("ID_CLIENT");
l.add("INFORMATION");
if (getTable().contains("ID_COMMERCIAL")) {
l.add("ID_COMMERCIAL");
}
return l;
}
 
54,10 → 60,8
@Override
public JComponent createEditor(String id) {
if (id.equals("INFORMATION")) {
final JTextArea jTextArea = new JTextArea();
jTextArea.setFont(new JLabel().getFont());
jTextArea.setMinimumSize(new Dimension(200, 150));
jTextArea.setPreferredSize(new Dimension(200, 150));
final ITextArea jTextArea = new ITextArea();
jTextArea.setRows(20);
return new JScrollPane(jTextArea);
} else if (id.equals("DATE")) {
return new JDate(true);
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/call/LeadCallSQLElement.java
7,7 → 7,6
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
 
import org.openconcerto.erp.modules.AbstractModule;
import org.openconcerto.erp.modules.ModuleElement;
15,6 → 14,7
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.ui.JDate;
import org.openconcerto.ui.JLabelBold;
import org.openconcerto.ui.component.ITextArea;
 
public class LeadCallSQLElement extends ModuleElement {
 
27,6 → 27,11
final List<String> l = new ArrayList<String>();
l.add("DATE");
l.add("ID_LEAD");
l.add("INFORMATION");
if (getTable().contains("ID_COMMERCIAL")) {
l.add("ID_COMMERCIAL");
}
 
return l;
}
 
54,10 → 59,8
@Override
public JComponent createEditor(String id) {
if (id.equals("INFORMATION")) {
final JTextArea jTextArea = new JTextArea();
jTextArea.setFont(new JLabel().getFont());
jTextArea.setMinimumSize(new Dimension(200, 150));
jTextArea.setPreferredSize(new Dimension(200, 150));
final ITextArea jTextArea = new ITextArea();
jTextArea.setRows(20);
return new JScrollPane(jTextArea);
} else if (id.equals("DATE")) {
return new JDate(true);
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/LeadSQLElement.java
4,6 → 4,7
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
54,6 → 55,38
super(module, Module.TABLE_LEAD);
 
// Call
final RowAction.PredicateRowAction addDuplicateAction = new RowAction.PredicateRowAction(new AbstractAction("Créer à partir de") {
 
@Override
public void actionPerformed(ActionEvent e) {
SQLRow sRow = IListe.get(e).getSelectedRow().asRow();
final SQLTable table = LeadSQLElement.this.getTable().getTable(Module.TABLE_LEAD);
final SQLElement leadElt = LeadSQLElement.this.getDirectory().getElement(table);
EditFrame editFrame = new EditFrame(leadElt);
final SQLRowValues sqlRowValues = new SQLRowValues(table);
sqlRowValues.put("COMPANY", sRow.getObject("COMPANY"));
sqlRowValues.put("PHONE", sRow.getObject("PHONE"));
sqlRowValues.put("FAX", sRow.getObject("FAX"));
sqlRowValues.put("WEBSITE", sRow.getObject("WEBSITE"));
sqlRowValues.put("DATE", new Date());
SQLRowValues adr = new SQLRowValues(sRow.getForeign("ID_ADRESSE").asRowValues());
sqlRowValues.put("ID_ADRESSE", adr);
sqlRowValues.put("INDUSTRY", sRow.getObject("INDUSTRY"));
sqlRowValues.put("REVENUE", sRow.getObject("REVENUE"));
sqlRowValues.put("EMPLOYEES", sRow.getObject("EMPLOYEES"));
sqlRowValues.put("LOCALISATION", sRow.getObject("LOCALISATION"));
sqlRowValues.put("SIRET", sRow.getObject("SIRET"));
sqlRowValues.put("APE", sRow.getObject("APE"));
 
editFrame.getSQLComponent().select(sqlRowValues);
FrameUtil.show(editFrame);
}
}, true) {
};
addDuplicateAction.setPredicate(IListeEvent.getSingleSelectionPredicate());
getRowActions().add(addDuplicateAction);
 
// Call
final RowAction.PredicateRowAction addCallAction = new RowAction.PredicateRowAction(new AbstractAction("Appeler") {
 
@Override
121,6 → 154,79
}
}
 
if (getTable().contains("MODIFICATION_DATE")) {
BaseSQLTableModelColumn dateM = new BaseSQLTableModelColumn("Date de modification", Date.class) {
 
@Override
protected Object show_(SQLRowAccessor r) {
return r.getObject("MODIFICATION_DATE");
}
 
@Override
public Set<FieldPath> getPaths() {
Path p = new Path(getTable());
return CollectionUtils.createSet(new FieldPath(p, "MODIFICATION_DATE"));
}
};
source.getColumns().add(1, dateM);
}
 
BaseSQLTableModelColumn dateV = new BaseSQLTableModelColumn("Visite", Date.class) {
 
@Override
protected Object show_(SQLRowAccessor r) {
Date d = null;
Collection<? extends SQLRowAccessor> l = r.getReferentRows(r.getTable().getTable("LEAD_VISIT"));
for (SQLRowAccessor sqlRowAccessor : l) {
if (d != null && sqlRowAccessor.getObject("DATE") != null && d.before(sqlRowAccessor.getDate("DATE").getTime())) {
d = sqlRowAccessor.getDate("DATE").getTime();
} else {
if (d == null && sqlRowAccessor.getObject("DATE") != null) {
d = sqlRowAccessor.getDate("DATE").getTime();
}
}
 
}
return d;
}
 
@Override
public Set<FieldPath> getPaths() {
Path p = new Path(getTable());
p = p.add(p.getLast().getTable("LEAD_VISIT"));
return CollectionUtils.createSet(new FieldPath(p, "DATE"));
}
};
source.getColumns().add(1, dateV);
 
BaseSQLTableModelColumn dateA = new BaseSQLTableModelColumn("Appel", Date.class) {
 
@Override
protected Object show_(SQLRowAccessor r) {
Date d = null;
Collection<? extends SQLRowAccessor> l = r.getReferentRows(r.getTable().getTable("LEAD_CALL"));
for (SQLRowAccessor sqlRowAccessor : l) {
if (d != null && sqlRowAccessor.getObject("DATE") != null && d.before(sqlRowAccessor.getDate("DATE").getTime())) {
d = sqlRowAccessor.getDate("DATE").getTime();
} else {
if (d == null && sqlRowAccessor.getObject("DATE") != null) {
d = sqlRowAccessor.getDate("DATE").getTime();
}
}
 
}
return d;
}
 
@Override
public Set<FieldPath> getPaths() {
Path p = new Path(getTable());
p = p.add(p.getLast().getTable("LEAD_CALL"));
return CollectionUtils.createSet(new FieldPath(p, "DATE"));
}
};
source.getColumns().add(1, dateA);
 
BaseSQLTableModelColumn adresse = new BaseSQLTableModelColumn("Adresse", String.class) {
 
@Override
189,6 → 295,32
};
source.getColumns().add(ville);
 
BaseSQLTableModelColumn dpt = new BaseSQLTableModelColumn("Département", String.class) {
 
@Override
protected Object show_(SQLRowAccessor r) {
 
String s = r.getForeign("ID_ADRESSE").getString("CODE_POSTAL");
if (s != null && s.length() >= 2) {
return s.substring(0, 2);
} else {
return s;
}
 
}
 
@Override
public Set<FieldPath> getPaths() {
Path p = new Path(getTable());
final SQLTable clientT = getTable().getForeignTable("ID_CLIENT");
p = p.add(clientT);
p = p.add(clientT.getField("ID_ADRESSE"));
return CollectionUtils.createSet(new FieldPath(p, "VILLE"), new FieldPath(p, "CODE_POSTAL"));
}
};
 
source.getColumns().add(dpt);
 
if (getTable().contains("REMIND_DATE")) {
BaseSQLTableModelColumn dateRemind = new BaseSQLTableModelColumn("Date de rappel", Date.class) {
 
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/translation_fr.xml
New file
0,0 → 1,4
<translation lang="fr">
<item id="customerrelationship.lead.items.call.tab" label="Appels" />
<item id="customerrelationship.lead.items.visit.tab" label="Visites" />
</translation>
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/LeadCustomerSQLInjector.java
3,6 → 3,8
*/
package org.openconcerto.modules.customerrelationship.lead;
 
import java.util.Date;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLInjector;
import org.openconcerto.sql.model.SQLRowAccessor;
26,6 → 28,8
map(leadTable.getField("MOBILE"), customerTable.getField("TEL_P"));
// map(leadTable.getField("INFORMATION"), customerTable.getField("INFOS"));
map(getSource().getField("INFOS"), getDestination().getField("INFOS"));
remove(leadTable.getField("DATE"), customerTable.getField("DATE"));
mapDefaultValues(customerTable.getField("DATE"), new Date());
remove(leadTable.getField("ID_ADRESSE"), customerTable.getField("ID_ADRESSE"));
}
 
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/labels_fr.xml
38,6 → 38,7
name="rapport d'appel prospect" namePlural="rapports d'appel prospect">
<FIELD name="DATE" label="Date de l'appel" />
<FIELD name="ID_LEAD" label="Entreprise" />
<FIELD name="ID_COMMERCIAL" label="Commercial" />
<FIELD name="INFORMATION" label="Résumé de l'appel" />
<FIELD name="NEXTCONTACT_DATE" label="Date de prochain contact" />
<FIELD name="NEXTCONTACT_INFO" label="Motif de prochain contact" />
47,6 → 48,7
</element>
<element refid="org.openconcerto.modules.customerrelationship.lead/CUSTOMER_CALL" nameClass="masculine"
name="rapport d'appel client" namePlural="rapports d'appel client">
<FIELD name="ID_COMMERCIAL" label="Commercial" />
<FIELD name="DATE" label="Date de l'appel" />
<FIELD name="ID_CLIENT" label="Client" />
<FIELD name="INFORMATION" label="Résumé de l'appel" />
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/LeadSQLComponent.java
5,13 → 5,17
 
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTextField;
 
import org.openconcerto.modules.customerrelationship.lead.visit.LeadActionItemTable;
import org.openconcerto.sql.element.GroupSQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLBackgroundTableCache;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.sqlobject.ElementComboBox;
import org.openconcerto.sql.sqlobject.SQLSearchableTextCombo;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.ui.JDate;
21,8 → 25,12
import org.openconcerto.ui.group.Group;
 
public class LeadSQLComponent extends GroupSQLComponent {
 
private LeadActionItemTable tableCall, tableVisit;
 
public LeadSQLComponent(SQLElement element, Group group) {
super(element, group);
startTabGroupAfter("customerrelationship.lead.state");
}
 
@Override
37,10 → 45,26
public JComponent getLabel(String id) {
if (id.equals("customerrelationship.lead.person")) {
return new JLabelBold("Contact");
}
if (id.equals("customerrelationship.lead.items.visit.tab")) {
return new JLabelBold("Visites");
}
if (id.equals("customerrelationship.lead.items.visit")) {
return new JLabelBold("");
}
if (id.equals("customerrelationship.lead.items.call.tab")) {
return new JLabelBold("Appels");
}
if (id.equals("customerrelationship.lead.items.call")) {
return new JLabelBold("");
} else if (id.equals("customerrelationship.lead.contact")) {
return new JLabel();
} else if (id.equals("customerrelationship.lead.address")) {
return new JLabelBold("Adresse");
} else if (id.equals("customerrelationship.lead.info")) {
return new JLabelBold("Infos");
} else if (id.equals("customerrelationship.lead.state")) {
return new JLabelBold("Statut");
} else {
return super.getLabel(id);
}
48,20 → 72,61
 
@Override
public JComponent createEditor(String id) {
 
if (id.equals("INFORMATION") || id.equals("INFOS")) {
final ITextArea jTextArea = new ITextArea();
final ITextArea jTextArea = new ITextArea(3,3);
jTextArea.setFont(new JLabel().getFont());
return jTextArea;
} else if (id.equals("ID_COMMERCIAL") || id.equals("ID_TITRE_PERSONNEL")) {
ElementComboBox comp = new ElementComboBox(false, 1);
((ElementComboBox) comp).init(getElement().getForeignElement(id));
return comp;
} else if (id.equals("customerrelationship.lead.items.call")) {
tableCall = new LeadActionItemTable(getElement().getDirectory().getElement(Module.TABLE_LEAD_CALL));
return tableCall;
} else if (id.equals("customerrelationship.lead.items.visit")) {
tableVisit = new LeadActionItemTable(getElement().getDirectory().getElement(Module.TABLE_LEAD_VISIT));
return tableVisit;
} else if (id.equals("INDUSTRY") || id.equals("STATUS") || id.equals("RATING") || id.equals("SOURCE") || id.equals("DISPO")) {
return new SQLSearchableTextCombo(ComboLockedMode.UNLOCKED, 1, 20, false);
return new SQLSearchableTextCombo(ComboLockedMode.UNLOCKED, 1, 1, false);
} else if (id.equals("DATE")) {
return new JDate(true);
}
return super.createEditor(id);
JComponent comp = super.createEditor(id);
if(comp.getClass() == JTextField.class) {
JTextField jtxt = new JTextField(10);
comp = jtxt;
}
 
return comp;
}
 
@Override
public int insert(SQLRow order) {
int id = super.insert(order);
this.tableCall.updateField("ID_LEAD", id);
this.tableVisit.updateField("ID_LEAD", id);
return id;
}
 
@Override
public void select(SQLRowAccessor r) {
super.select(r);
if (r != null) {
this.tableCall.insertFrom("ID_LEAD", r.getID());
this.tableVisit.insertFrom("ID_LEAD", r.getID());
}
}
 
@Override
public void update() {
super.update();
this.tableCall.updateField("ID_LEAD", getSelectedID());
this.tableVisit.updateField("ID_LEAD", getSelectedID());
}
 
@Override
protected SQLRowValues createDefaults() {
SQLRowValues rowVals = new SQLRowValues(getTable());
rowVals.put("STATUS", "Nouveau");
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/visit/CustomerVisitSQLElement.java
28,6 → 28,10
final List<String> l = new ArrayList<String>();
l.add("DATE");
l.add("ID_CLIENT");
l.add("INFORMATION");
if (getTable().contains("ID_COMMERCIAL")) {
l.add("ID_COMMERCIAL");
}
l.add("NEXTCONTACT_DATE");
return l;
}
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/visit/LeadActionItemTable.java
New file
0,0 → 1,106
package org.openconcerto.modules.customerrelationship.lead.visit;
 
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.io.File;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Vector;
 
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ToolTipManager;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLBackgroundTableCache;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.view.list.RowValuesTable;
import org.openconcerto.sql.view.list.RowValuesTableControlPanel;
import org.openconcerto.sql.view.list.RowValuesTableModel;
import org.openconcerto.sql.view.list.RowValuesTableRenderer;
import org.openconcerto.sql.view.list.SQLTableElement;
import org.openconcerto.ui.table.TimestampTableCellEditor;
 
public class LeadActionItemTable extends JPanel {
 
private RowValuesTable table;
final RowValuesTableControlPanel comp;
 
public LeadActionItemTable(SQLElement elt) {
this.setOpaque(false);
this.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = 1;
c.gridheight = 1;
c.gridx = 0;
c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.weighty = 0;
 
List<SQLTableElement> list = new Vector<SQLTableElement>();
 
SQLTableElement tableElementDateL = new SQLTableElement(elt.getTable().getField("DATE"), Timestamp.class, new TimestampTableCellEditor());
list.add(tableElementDateL);
 
SQLTableElement tableElementTps = new SQLTableElement(elt.getTable().getField("INFORMATION"));
list.add(tableElementTps);
 
if (elt.getTable().contains("ID_COMMERCIAL")) {
SQLTableElement tableElementCom = new SQLTableElement(elt.getTable().getField("ID_COMMERCIAL"));
list.add(tableElementCom);
}
 
SQLRowValues rowValsDefault = new SQLRowValues(elt.getTable());
rowValsDefault.put("DATE", new Date());
if (elt.getTable().contains("ID_COMMERCIAL")) {
final int idUser = UserManager.getInstance().getCurrentUser().getId();
final SQLElement eltComm = elt.getForeignElement("ID_COMMERCIAL");
SQLRow rowsComm = SQLBackgroundTableCache.getInstance().getCacheForTable(eltComm.getTable()).getFirstRowContains(idUser, eltComm.getTable().getField("ID_USER_COMMON"));
 
if (rowsComm != null) {
rowValsDefault.put("ID_COMMERCIAL", rowsComm.getID());
}
}
final RowValuesTableModel model = new RowValuesTableModel(elt, list, elt.getTable().getField("DATE"), false, rowValsDefault);
 
this.table = new RowValuesTable(model, new File(Configuration.getInstance().getConfDir(), "Table" + File.separator + "Table_LeadActionItemTable.xml"));
ToolTipManager.sharedInstance().unregisterComponent(this.table);
ToolTipManager.sharedInstance().unregisterComponent(this.table.getTableHeader());
this.comp = new RowValuesTableControlPanel(this.table);
this.add(this.comp, c);
 
c.gridy++;
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.weighty = 1;
this.add(new JScrollPane(this.table), c);
this.table.setDefaultRenderer(Long.class, new RowValuesTableRenderer());
}
 
public void updateField(String field, int id) {
this.table.updateField(field, id);
}
 
public void insertFrom(String field, SQLRowValues row) {
this.table.insertFrom(field, row);
}
 
public void insertFrom(String field, int id) {
this.table.insertFrom(field, id);
}
 
public RowValuesTableModel getModel() {
return this.table.getRowValuesTableModel();
}
 
public void setEditable(boolean b) {
this.comp.setEditable(b);
this.table.setEditable(b);
}
 
}
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/visit/LeadVisitSQLElement.java
28,6 → 28,11
final List<String> l = new ArrayList<String>();
l.add("DATE");
l.add("ID_LEAD");
l.add("INFORMATION");
if (getTable().contains("ID_COMMERCIAL")) {
l.add("ID_COMMERCIAL");
}
 
l.add("NEXTCONTACT_DATE");
return l;
}
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/importer/LeadImporter.java
19,10 → 19,15
import org.openconcerto.openoffice.spreadsheet.Sheet;
import org.openconcerto.openoffice.spreadsheet.SpreadSheet;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesCluster;
import org.openconcerto.sql.model.SQLRowValuesCluster.StoreMode;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLRowValuesCluster.StoreMode;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.utils.cc.IdentityHashSet;
import org.openconcerto.utils.text.CSVReader;
import org.openconcerto.utils.text.CharsetHelper;
76,6 → 81,82
public abstract T convert(String[] line);
}
 
public Map<Object, LeadCSV> exportLeads(SQLTable tableLead, File dir2save, File sheetFile) throws Exception {
List<String[]> adresse = new ArrayList<String[]>();
List<String[]> leadList = new ArrayList<String[]>();
 
Map<Object, LeadCSV> leadMap = new HashMap<Object, LeadCSV>();
 
SQLSelect sel = new SQLSelect();
sel.addSelectStar(tableLead);
List<SQLRow> leads = SQLRowListRSH.execute(sel);
int i = 1;
for (SQLRow lead : leads) {
 
int idAdr = adresse.size();
AdresseCSV adr = createAdresse(i, lead.getForeign("ID_ADRESSE"));
adresse.add(adr.toCSVLine());
LeadCSV leadCsv = createLeadFromRow(i, lead, Integer.valueOf(adr.getId().toString()));
leadList.add(leadCsv.toCSVLine());
leadMap.put(leadCsv.getId(), leadCsv);
i++;
}
 
DataImporter importer = new DataImporter(tableLead);
final File csvFile = new File(dir2save, "Lead.csv");
csvFile.createNewFile();
importer.exportModelToCSV(csvFile, leadList);
DataImporter importerAdr = new DataImporter(tableLead.getForeignTable("ID_ADRESSE"));
final File csvFile2 = new File(dir2save, "Address.csv");
csvFile2.createNewFile();
importerAdr.exportModelToCSV(csvFile2, adresse);
 
return leadMap;
}
 
public AdresseCSV createAdresse(int i, SQLRow rowAdr) {
 
String street = rowAdr.getString("RUE");
final String ville = rowAdr.getString("VILLE");
final String cp = rowAdr.getString("CODE_POSTAL");
 
AdresseCSV adrLine = new AdresseCSV(i, street, ville, cp);
 
return adrLine;
}
 
public LeadCSV createLeadFromRow(int i, SQLRowAccessor row, int idAdr) {
 
LeadCSV leadLine = new LeadCSV(i, row.getString("COMPANY"), "");
 
leadLine.setIdAdr(idAdr);
 
leadLine.setPhone(row.getString("PHONE"));
leadLine.setMail(row.getString("EMAIL"));
leadLine.setCell(row.getString("MOBILE"));
leadLine.setFax(row.getString("FAX"));
leadLine.setContact(row.getString("NAME"));
leadLine.setLocalisation(row.getString("LOCALISATION"));
leadLine.setSecteur(row.getString("INDUSTRY"));
leadLine.setEffectif(String.valueOf(row.getInt("EMPLOYEES")));
leadLine.setOrigine(row.getString("SOURCE"));
 
leadLine.setSiret(row.getString("SIRET"));
leadLine.setApe(row.getString("APE"));
 
leadLine.setNom(row.getString("NAME"));
leadLine.setPrenom(row.getString("FIRSTNAME"));
leadLine.setDesc(row.getString("INFORMATION"));
// rowVals.put("REVENUE", (getBudget().trim().length()==0?0:Integer);
leadLine.setDispo(row.getString("DISPO"));
leadLine.setTypeT(row.getString("INDUSTRY"));
leadLine.setStatut(row.getString("STATUS"));
leadLine.setInfos(row.getString("INFOS"));
 
return leadLine;
 
}
 
public LeadCSV createLead(int i, Sheet sheet, int idAdr, int id) {
final Cell<SpreadSheet> cell0 = sheet.getImmutableCellAt(0, i);
final String societeName = cell0.getValue().toString().trim();
/trunk/Modules/Module Lead/src/org/openconcerto/modules/customerrelationship/lead/LeadGroup.java
1,6 → 1,5
package org.openconcerto.modules.customerrelationship.lead;
 
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.users.rights.UserRights;
import org.openconcerto.sql.users.rights.UserRightsManager;
import org.openconcerto.ui.group.Group;
11,52 → 10,59
public LeadGroup() {
super("customerrelationship.lead.default");
final Group g = new Group("customerrelationship.lead.identifier");
g.addItem("NUMBER");
g.addItem("DATE");
g.addItem("NUMBER", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
g.addItem("DATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
g.addItem("COMPANY", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(g);
 
final Group gContact = new Group("customerrelationship.lead.person", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gContact.addItem("NAME");
gContact.addItem("FIRSTNAME");
gContact.addItem("ID_TITRE_PERSONNEL");
gContact.addItem("ID_TITRE_PERSONNEL", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gContact.addItem("NAME", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gContact.addItem("FIRSTNAME", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(gContact);
 
final Group gCustomer = new Group("customerrelationship.lead.contact", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gCustomer.addItem("ROLE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gCustomer.addItem("PHONE");
gCustomer.addItem("MOBILE");
gCustomer.addItem("FAX");
gCustomer.addItem("PHONE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gCustomer.addItem("MOBILE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gCustomer.addItem("FAX", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gCustomer.addItem("EMAIL", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gCustomer.addItem("WEBSITE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(gCustomer);
 
final Group gAddress = new Group("customerrelationship.lead.address", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gAddress.addItem("ID_ADRESSE");
gAddress.addItem("ID_ADRESSE", LayoutHints.DEFAULT_VERY_LARGE_FIELD_HINTS);
this.add(gAddress);
 
final Group gInfos = new Group("customerrelationship.lead.info");
gInfos.addItem("INFORMATION", new LayoutHints(true, true, true, true, true, true));
gInfos.addItem("INDUSTRY");
gInfos.addItem("REVENUE");
gInfos.addItem("EMPLOYEES");
gInfos.addItem("INFOS", new LayoutHints(true, true, true, true, true, true));
final Group gInfos = new Group("customerrelationship.lead.info", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gInfos.addItem("INFORMATION", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gInfos.addItem("INDUSTRY", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gInfos.addItem("REVENUE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gInfos.addItem("EMPLOYEES", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gInfos.addItem("INFOS", new LayoutHints(true, true, true, true, true, true, true, true));
this.add(gInfos);
 
final Group gState = new Group("customerrelationship.lead.state");
gState.addItem("RATING");
gState.addItem("SOURCE");
gState.addItem("STATUS");
gState.addItem("ID_COMMERCIAL");
gState.addItem("REMIND_DATE");
final Group gState = new Group("customerrelationship.lead.state", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gState.addItem("RATING", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gState.addItem("SOURCE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gState.addItem("STATUS", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gState.addItem("ID_COMMERCIAL", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gState.addItem("REMIND_DATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
UserRights rights = UserRightsManager.getCurrentUserRights();
if (rights.haveRight("CLIENT_PROSPECT")) {
gState.addItem("ID_CLIENT");
gState.addItem("ID_CLIENT", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
}
gState.addItem("DISPO");
 
gState.addItem("DISPO", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(gState);
 
final Group gItems = new Group("customerrelationship.lead.items.call.tab");
gItems.addItem("customerrelationship.lead.items.call", LayoutHints.DEFAULT_VERY_LARGE_TEXT_HINTS);
this.add(gItems);
 
final Group gItems2 = new Group("customerrelationship.lead.items.visit.tab");
gItems2.addItem("customerrelationship.lead.items.visit", LayoutHints.DEFAULT_VERY_LARGE_TEXT_HINTS);
this.add(gItems2);
 
}
 
public static void main(String[] args) {
/trunk/Modules/Module Project/src/org/openconcerto/modules/project/Module.java
94,7 → 94,6
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.IExnClosure;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.i18n.TranslationManager;
 
public final class Module extends AbstractModule {
 
191,7 → 190,6
@Override
protected void setupElements(final SQLElementDirectory dir) {
super.setupElements(dir);
TranslationManager.getInstance().addTranslationStreamFromClass(this.getClass());
dir.addSQLElement(ProjectSQLElement.class);
dir.addSQLElement(ProjectStateSQLElement.class);
dir.addSQLElement(ProjectTypeSQLElement.class);
607,7 → 605,7
protected void setupComponents(final ComponentsContext ctxt) {
 
DBRoot root = ComptaPropsConfiguration.getInstanceCompta().getRootSociete();
List<String> table2check = Arrays.asList("BON_RECEPTION", "DEMANDE_PRIX", "DEMANDE_ACHAT_ELEMENT");
List<String> table2check = Arrays.asList("BON_RECEPTION", "DEMANDE_PRIX", "DEMANDE_ACHAT_ELEMENT", "FACTURE_FOURNISSEUR");
for (String table : table2check) {
if (root.contains(table)) {
SQLTable tableCR = root.getTable(table);
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/ExpenseSQLComponent.java
7,6 → 7,7
import java.util.Set;
 
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.text.AbstractDocument;
import javax.swing.text.JTextComponent;
 
13,9 → 14,10
import org.openconcerto.erp.core.common.ui.DeviseField;
import org.openconcerto.sql.element.GroupSQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.sqlobject.ElementComboBox;
import org.openconcerto.sql.sqlobject.itemview.VWRowItemView;
import org.openconcerto.ui.JDate;
import org.openconcerto.ui.component.ITextArea;
import org.openconcerto.ui.JLabelBold;
import org.openconcerto.ui.component.text.TextComponentUtils;
import org.openconcerto.ui.group.Group;
import org.openconcerto.utils.text.DocumentFilterList;
41,11 → 43,13
}
};
 
final AbstractDocument comp1 = (AbstractDocument) TextComponentUtils.getDocument(getView("TRAVEL_DISTANCE").getComp());
final AbstractDocument comp1 = (AbstractDocument) TextComponentUtils
.getDocument(getView("TRAVEL_DISTANCE").getComp());
DocumentFilterList.add(comp1, new LimitedSizeDocumentFilter(5), FilterType.SIMPLE_FILTER);
getView("TRAVEL_DISTANCE").addValueListener(listener);
 
final AbstractDocument comp2 = (AbstractDocument) TextComponentUtils.getDocument(getView("TRAVEL_RATE").getComp());
final AbstractDocument comp2 = (AbstractDocument) TextComponentUtils
.getDocument(getView("TRAVEL_RATE").getComp());
DocumentFilterList.add(comp2, new LimitedSizeDocumentFilter(5), FilterType.SIMPLE_FILTER);
 
getView("TRAVEL_RATE").addValueListener(listener);
62,17 → 66,33
}
 
@Override
public JComponent getEditor(String id) {
if (id.equals("DESCRIPTION")) {
return new ITextArea();
} else if (id.equals("DATE")) {
public JComponent createEditor(String id) {
if(id.equals("ID_USER_COMMON") || id.equals("ID_EXPENSE_STATE")) {
ElementComboBox comp = new ElementComboBox(false, 15);
((ElementComboBox) comp).init(getElement().getForeignElement(id));
return comp;
}
else if (id.equals("DATE")) {
return new JDate(true);
} else if (id.endsWith("AMOUNT")) {
return new DeviseField();
}
return super.getEditor(id);
return super.createEditor(id);
}
 
@Override
public JLabel getLabel(String id) {
if (id.equals("humanresources.travel.expense.travel")) {
return new JLabelBold("Frais kilometriques");
} else if (id.equals("humanresources.travel.expense.default")) {
return new JLabelBold("Descriptif");
} else if (id.equals("humanresources.travel.expense.misc")) {
return new JLabelBold("Frais annexes");
} else {
return (JLabel) super.getLabel(id);
}
}
 
private void updateAmount() {
float v1 = getFloat("TRAVEL_DISTANCE");
float v2 = getFloat("TRAVEL_RATE");
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/ExpenseGroup.java
7,32 → 7,31
public class ExpenseGroup extends Group {
 
public ExpenseGroup() {
super(ExpenseSQLElement.ELEMENT_CODE + ".default");
super(ExpenseSQLElement.ELEMENT_CODE + ".default", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
final Group g = new Group(ExpenseSQLElement.ELEMENT_CODE + ".identifier");
g.addItem("DATE");
g.addItem("ID_USER_COMMON");
g.addItem("DATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
g.addItem("ID_USER_COMMON", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
 
this.add(g);
 
final Group gDescription = new Group(ExpenseSQLElement.ELEMENT_CODE + ".description");
gDescription.add(new Item("DESCRIPTION", new LayoutHints(true, true, true, true, true, true)));
gDescription.addItem("DESCRIPTION", new LayoutHints(true, true, true, true, true, true, true, true));
gDescription.addItem("ID_EXPENSE_STATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(gDescription);
 
final Group gTravel = new Group(ExpenseSQLElement.ELEMENT_CODE + ".travel", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gTravel.addItem("TRAVEL_DISTANCE");
gTravel.addItem("TRAVEL_RATE");
gTravel.addItem("TRAVEL_AMOUNT");
final Group gTravel = new Group(ExpenseSQLElement.ELEMENT_CODE + ".travel",
LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gTravel.addItem("TRAVEL_DISTANCE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gTravel.addItem("TRAVEL_RATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
gTravel.addItem("TRAVEL_AMOUNT", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(gTravel);
 
final Group gAddress = new Group(ExpenseSQLElement.ELEMENT_CODE + ".misc", LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gAddress.addItem("MISC_AMOUNT");
final Group gAddress = new Group(ExpenseSQLElement.ELEMENT_CODE + ".misc",
LayoutHints.DEFAULT_SEPARATED_GROUP_HINTS);
gAddress.addItem("MISC_AMOUNT", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
// gAddress.addItem("ID_EXPENSE_STATE", LayoutHints.DEFAULT_LARGE_FIELD_HINTS);
this.add(gAddress);
 
final Group gState = new Group(ExpenseSQLElement.ELEMENT_CODE + ".state");
gState.addItem("ID_EXPENSE_STATE");
 
this.add(gState);
 
}
 
}
/trunk/Modules/Module Expense/src/org/openconcerto/modules/humanresources/travel/expense/translation_fr.xml
New file
0,0 → 1,5
<translation lang="fr">
<item id="humanresources.travel.expense.travel" label="Frais kilometriques" />
<item id="humanresources.travel.expense.misc" label="Frais annexes" />
<item id="humanresources.travel.expense.default" label="Description" />
</translation>
/trunk/Modules/Module 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 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 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 Label/lib/barcode4j-2.1.0.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/lib/barcode4j-2.1.0.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/lib/jbarcode-0.2.8.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/lib/jbarcode-0.2.8.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Logmars.java
New file
0,0 → 1,98
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
/**
* Implements the LOGMARS (Logistics Applications of Automated Marking and Reading Symbols) standard
* used by the US Department of Defense. Input data can be of any length and supports the characters
* 0-9, A-Z, dash (-), full stop (.), space, dollar ($), slash (/), plus (+) and percent (%). A
* Modulo-43 check digit is calculated and added, and should not form part of the input data.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Logmars extends Symbol {
 
private static final String[] CODE39LM = { "1113313111", "3113111131", "1133111131", "3133111111", "1113311131", "3113311111", "1133311111", "1113113131", "3113113111", "1133113111", "3111131131",
"1131131131", "3131131111", "1111331131", "3111331111", "1131331111", "1111133131", "3111133111", "1131133111", "1111333111", "3111111331", "1131111331", "3131111311", "1111311331",
"3111311311", "1131311311", "1111113331", "3111113311", "1131113311", "1111313311", "3311111131", "1331111131", "3331111111", "1311311131", "3311311111", "1331311111", "1311113131",
"3311113111", "1331113111", "1313131111", "1313111311", "1311131311", "1113131311" };
 
private static final char[] LOOKUP = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%' };
 
/** Ratio of wide bar width to narrow bar width. */
private double moduleWidthRatio = 3;
 
/**
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually between
* {@code 2} and {@code 3}. The default value is {@code 3}.
*
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width
*/
public void setModuleWidthRatio(final double moduleWidthRatio) {
this.moduleWidthRatio = moduleWidthRatio;
}
 
/**
* Returns the ratio of wide bar width to narrow bar width.
*
* @return the ratio of wide bar width to narrow bar width
*/
public double getModuleWidthRatio() {
return this.moduleWidthRatio;
}
 
/** {@inheritDoc} */
@Override
protected double getModuleWidth(final int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return this.moduleWidthRatio;
}
}
 
/** {@inheritDoc} */
@Override
protected void encode() {
 
if (!this.content.matches("[0-9A-Z\\. \\-$/+%]*")) {
throw new OkapiException("Invalid characters in input");
}
 
String p = "";
final int l = this.content.length();
int charval, counter = 0;
char thischar;
char checkDigit;
for (int i = 0; i < l; i++) {
thischar = this.content.charAt(i);
charval = positionOf(thischar, LOOKUP);
counter += charval;
p += CODE39LM[charval];
}
 
counter = counter % 43;
checkDigit = LOOKUP[counter];
infoLine("Check Digit: " + checkDigit);
p += CODE39LM[counter];
 
this.readable = this.content + checkDigit;
this.pattern = new String[] { "1311313111" + p + "131131311" };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Pdf417.java
New file
0,0 → 1,1531
/*
* Copyright 2014-2017 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;
 
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
 
/**
* <p>
* Implements PDF417 bar code symbology and MicroPDF417 bar code symbology according to ISO/IEC
* 15438:2006 and ISO/IEC 24728:2006 respectively.
*
* <p>
* PDF417 supports encoding up to the ISO standard maximum symbol size of 925 codewords which (at
* error correction level 0) allows a maximum data size of 1850 text characters, or 2710 digits. The
* maximum size MicroPDF417 symbol can hold 250 alphanumeric characters or 366 digits.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class Pdf417 extends Symbol {
 
public enum Mode {
/** Normal PDF417. */
NORMAL,
/** Truncated PDF417. */
TRUNCATED,
/** MicroPDF417. */
MICRO
}
 
private enum EncodingMode {
FALSE, TEX, BYT, NUM
}
 
private final int[] codeWords = new int[2700];
private int codeWordCount;
private Mode symbolMode = Mode.NORMAL;
private Integer columns;
private Integer rows;
private int preferredEccLevel = -1;
private int structuredAppendFileId = 0;
private int structuredAppendPosition = 1;
private int structuredAppendTotal = 1;
 
private static final int MAX_NUMERIC_COMPACTION_BLOCK_SIZE = 44;
 
private static final int[] COEFRS = {
/* k = 2 */
27, 917,
 
/* k = 4 */
522, 568, 723, 809,
 
/* k = 8 */
237, 308, 436, 284, 646, 653, 428, 379,
 
/* k = 16 */
274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65,
 
/* k = 32 */
361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410,
 
/* k = 64 */
539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, 381, 612, 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, 400, 672, 762, 283, 184, 440, 35, 519, 31, 460, 594,
225, 535, 517, 352, 605, 158, 651, 201, 488, 502, 648, 733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843, 623, 264, 543,
 
/* k = 128 */
521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, 749, 415, 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, 292, 908, 490, 704, 516, 258, 457, 907, 594, 723,
674, 292, 272, 96, 684, 432, 686, 606, 860, 569, 193, 219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712, 463, 646, 776, 171, 491, 297, 763, 156, 732, 95, 270, 447, 90, 507, 48,
228, 821, 808, 898, 784, 663, 627, 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, 157, 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, 804, 34, 211, 330,
539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539,
 
/* k = 256 */
524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, 138, 720, 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, 280, 201, 280, 828, 757, 710, 814, 919, 89, 68, 569, 11,
204, 796, 605, 540, 913, 801, 700, 799, 137, 439, 418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284, 549, 209, 884, 315, 70, 329, 793, 490, 274, 877, 162, 749, 812, 684, 461, 334,
376, 849, 521, 307, 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470, 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, 538, 906, 90, 2, 290, 743, 199,
655, 903, 329, 49, 802, 580, 355, 588, 188, 462, 10, 134, 628, 320, 479, 130, 739, 71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234, 722, 384, 177, 752, 607, 640, 455, 193, 689, 707,
805, 641, 48, 60, 732, 621, 895, 544, 261, 852, 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528, 503, 118, 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550, 73,
914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, 893, 754, 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, 922, 191, 910, 532, 609, 829, 189, 20, 167, 29, 872, 449,
83, 402, 41, 656, 505, 579, 481, 173, 404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10,
 
/* k = 512 */
352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, 350, 492, 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, 87, 193, 352, 781, 846, 75, 327, 520, 435, 543,
203, 666, 249, 346, 781, 621, 640, 268, 794, 534, 539, 781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858, 916, 552, 41, 542, 289, 122, 272, 383, 800, 485, 98, 752, 472, 761, 107,
784, 860, 658, 741, 290, 204, 681, 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142, 808, 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, 258, 240, 518,
794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, 596, 786, 303, 570, 381, 415, 641, 156, 237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402, 40, 708, 575, 162, 864, 229, 65, 861,
841, 512, 164, 477, 221, 92, 358, 785, 288, 357, 850, 836, 827, 736, 707, 94, 8, 494, 114, 521, 2, 499, 851, 543, 152, 729, 771, 95, 248, 361, 578, 323, 856, 797, 289, 51, 684, 466, 533,
820, 669, 45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578, 37, 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, 911, 283, 711, 472, 420, 245,
288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408, 842, 383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713, 159, 672, 729, 624, 59, 193, 417, 158, 209, 563, 564, 343, 693, 109,
608, 563, 365, 181, 772, 677, 310, 248, 353, 708, 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777, 618, 586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45, 787,
680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, 905, 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341, 242, 797, 838, 837, 720, 224, 307,
631, 61, 87, 560, 310, 756, 665, 397, 808, 851, 309, 473, 795, 378, 31, 647, 915, 459, 806, 590, 731, 425, 216, 548, 249, 321, 881, 699, 535, 673, 782, 210, 815, 905, 303, 843, 922, 281,
73, 469, 791, 660, 162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437, 375, 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, 842, 37, 357, 720,
742, 330, 5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316, 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721, 610, 46, 656, 447, 171, 616, 464, 190, 531, 297, 321, 762,
752, 533, 175, 134, 14, 381, 433, 717, 45, 111, 20, 596, 284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780, 407, 164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768, 223, 849,
647, 63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263 };
 
private static final String[] CODAGEMC = { "urA", "xfs", "ypy", "unk", "xdw", "yoz", "pDA", "uls", "pBk", "eBA", "pAs", "eAk", "prA", "uvs", "xhy", "pnk", "utw", "xgz", "fDA", "pls", "fBk", "frA",
"pvs", "uxy", "fnk", "ptw", "uwz", "fls", "psy", "fvs", "pxy", "ftw", "pwz", "fxy", "yrx", "ufk", "xFw", "ymz", "onA", "uds", "xEy", "olk", "ucw", "dBA", "oks", "uci", "dAk", "okg", "dAc",
"ovk", "uhw", "xaz", "dnA", "ots", "ugy", "dlk", "osw", "ugj", "dks", "osi", "dvk", "oxw", "uiz", "dts", "owy", "dsw", "owj", "dxw", "oyz", "dwy", "dwj", "ofA", "uFs", "xCy", "odk", "uEw",
"xCj", "clA", "ocs", "uEi", "ckk", "ocg", "ckc", "ckE", "cvA", "ohs", "uay", "ctk", "ogw", "uaj", "css", "ogi", "csg", "csa", "cxs", "oiy", "cww", "oij", "cwi", "cyy", "oFk", "uCw", "xBj",
"cdA", "oEs", "uCi", "cck", "oEg", "uCb", "ccc", "oEa", "ccE", "oED", "chk", "oaw", "uDj", "cgs", "oai", "cgg", "oab", "cga", "cgD", "obj", "cib", "cFA", "oCs", "uBi", "cEk", "oCg", "uBb",
"cEc", "oCa", "cEE", "oCD", "cEC", "cas", "cag", "caa", "cCk", "uAr", "oBa", "oBD", "cCB", "tfk", "wpw", "yez", "mnA", "tds", "woy", "mlk", "tcw", "woj", "FBA", "mks", "FAk", "mvk", "thw",
"wqz", "FnA", "mts", "tgy", "Flk", "msw", "Fks", "Fkg", "Fvk", "mxw", "tiz", "Fts", "mwy", "Fsw", "Fsi", "Fxw", "myz", "Fwy", "Fyz", "vfA", "xps", "yuy", "vdk", "xow", "yuj", "qlA", "vcs",
"xoi", "qkk", "vcg", "xob", "qkc", "vca", "mfA", "tFs", "wmy", "qvA", "mdk", "tEw", "wmj", "qtk", "vgw", "xqj", "hlA", "Ekk", "mcg", "tEb", "hkk", "qsg", "hkc", "EvA", "mhs", "tay", "hvA",
"Etk", "mgw", "taj", "htk", "qww", "vij", "hss", "Esg", "hsg", "Exs", "miy", "hxs", "Eww", "mij", "hww", "qyj", "hwi", "Eyy", "hyy", "Eyj", "hyj", "vFk", "xmw", "ytj", "qdA", "vEs", "xmi",
"qck", "vEg", "xmb", "qcc", "vEa", "qcE", "qcC", "mFk", "tCw", "wlj", "qhk", "mEs", "tCi", "gtA", "Eck", "vai", "tCb", "gsk", "Ecc", "mEa", "gsc", "qga", "mED", "EcC", "Ehk", "maw", "tDj",
"gxk", "Egs", "mai", "gws", "qii", "mab", "gwg", "Ega", "EgD", "Eiw", "mbj", "gyw", "Eii", "gyi", "Eib", "gyb", "gzj", "qFA", "vCs", "xli", "qEk", "vCg", "xlb", "qEc", "vCa", "qEE", "vCD",
"qEC", "qEB", "EFA", "mCs", "tBi", "ghA", "EEk", "mCg", "tBb", "ggk", "qag", "vDb", "ggc", "EEE", "mCD", "ggE", "qaD", "ggC", "Eas", "mDi", "gis", "Eag", "mDb", "gig", "qbb", "gia", "EaD",
"giD", "gji", "gjb", "qCk", "vBg", "xkr", "qCc", "vBa", "qCE", "vBD", "qCC", "qCB", "ECk", "mBg", "tAr", "gak", "ECc", "mBa", "gac", "qDa", "mBD", "gaE", "ECC", "gaC", "ECB", "EDg", "gbg",
"gba", "gbD", "vAq", "vAn", "qBB", "mAq", "EBE", "gDE", "gDC", "gDB", "lfA", "sps", "wey", "ldk", "sow", "ClA", "lcs", "soi", "Ckk", "lcg", "Ckc", "CkE", "CvA", "lhs", "sqy", "Ctk", "lgw",
"sqj", "Css", "lgi", "Csg", "Csa", "Cxs", "liy", "Cww", "lij", "Cwi", "Cyy", "Cyj", "tpk", "wuw", "yhj", "ndA", "tos", "wui", "nck", "tog", "wub", "ncc", "toa", "ncE", "toD", "lFk", "smw",
"wdj", "nhk", "lEs", "smi", "atA", "Cck", "tqi", "smb", "ask", "ngg", "lEa", "asc", "CcE", "asE", "Chk", "law", "snj", "axk", "Cgs", "trj", "aws", "nii", "lab", "awg", "Cga", "awa", "Ciw",
"lbj", "ayw", "Cii", "ayi", "Cib", "Cjj", "azj", "vpA", "xus", "yxi", "vok", "xug", "yxb", "voc", "xua", "voE", "xuD", "voC", "nFA", "tms", "wti", "rhA", "nEk", "xvi", "wtb", "rgk", "vqg",
"xvb", "rgc", "nEE", "tmD", "rgE", "vqD", "nEB", "CFA", "lCs", "sli", "ahA", "CEk", "lCg", "slb", "ixA", "agk", "nag", "tnb", "iwk", "rig", "vrb", "lCD", "iwc", "agE", "naD", "iwE", "CEB",
"Cas", "lDi", "ais", "Cag", "lDb", "iys", "aig", "nbb", "iyg", "rjb", "CaD", "aiD", "Cbi", "aji", "Cbb", "izi", "ajb", "vmk", "xtg", "ywr", "vmc", "xta", "vmE", "xtD", "vmC", "vmB", "nCk",
"tlg", "wsr", "rak", "nCc", "xtr", "rac", "vna", "tlD", "raE", "nCC", "raC", "nCB", "raB", "CCk", "lBg", "skr", "aak", "CCc", "lBa", "iik", "aac", "nDa", "lBD", "iic", "rba", "CCC", "iiE",
"aaC", "CCB", "aaB", "CDg", "lBr", "abg", "CDa", "ijg", "aba", "CDD", "ija", "abD", "CDr", "ijr", "vlc", "xsq", "vlE", "xsn", "vlC", "vlB", "nBc", "tkq", "rDc", "nBE", "tkn", "rDE", "vln",
"rDC", "nBB", "rDB", "CBc", "lAq", "aDc", "CBE", "lAn", "ibc", "aDE", "nBn", "ibE", "rDn", "CBB", "ibC", "aDB", "ibB", "aDq", "ibq", "ibn", "xsf", "vkl", "tkf", "nAm", "nAl", "CAo", "aBo",
"iDo", "CAl", "aBl", "kpk", "BdA", "kos", "Bck", "kog", "seb", "Bcc", "koa", "BcE", "koD", "Bhk", "kqw", "sfj", "Bgs", "kqi", "Bgg", "kqb", "Bga", "BgD", "Biw", "krj", "Bii", "Bib", "Bjj",
"lpA", "sus", "whi", "lok", "sug", "loc", "sua", "loE", "suD", "loC", "BFA", "kms", "sdi", "DhA", "BEk", "svi", "sdb", "Dgk", "lqg", "svb", "Dgc", "BEE", "kmD", "DgE", "lqD", "BEB", "Bas",
"kni", "Dis", "Bag", "knb", "Dig", "lrb", "Dia", "BaD", "Bbi", "Dji", "Bbb", "Djb", "tuk", "wxg", "yir", "tuc", "wxa", "tuE", "wxD", "tuC", "tuB", "lmk", "stg", "nqk", "lmc", "sta", "nqc",
"tva", "stD", "nqE", "lmC", "nqC", "lmB", "nqB", "BCk", "klg", "Dak", "BCc", "str", "bik", "Dac", "lna", "klD", "bic", "nra", "BCC", "biE", "DaC", "BCB", "DaB", "BDg", "klr", "Dbg", "BDa",
"bjg", "Dba", "BDD", "bja", "DbD", "BDr", "Dbr", "bjr", "xxc", "yyq", "xxE", "yyn", "xxC", "xxB", "ttc", "wwq", "vvc", "xxq", "wwn", "vvE", "xxn", "vvC", "ttB", "vvB", "llc", "ssq", "nnc",
"llE", "ssn", "rrc", "nnE", "ttn", "rrE", "vvn", "llB", "rrC", "nnB", "rrB", "BBc", "kkq", "DDc", "BBE", "kkn", "bbc", "DDE", "lln", "jjc", "bbE", "nnn", "BBB", "jjE", "rrn", "DDB", "jjC",
"BBq", "DDq", "BBn", "bbq", "DDn", "jjq", "bbn", "jjn", "xwo", "yyf", "xwm", "xwl", "tso", "wwf", "vto", "xwv", "vtm", "tsl", "vtl", "lko", "ssf", "nlo", "lkm", "rno", "nlm", "lkl", "rnm",
"nll", "rnl", "BAo", "kkf", "DBo", "lkv", "bDo", "DBm", "BAl", "jbo", "bDm", "DBl", "jbm", "bDl", "jbl", "DBv", "jbv", "xwd", "vsu", "vst", "nku", "rlu", "rlt", "DAu", "bBu", "jDu", "jDt",
"ApA", "Aok", "keg", "Aoc", "AoE", "AoC", "Aqs", "Aqg", "Aqa", "AqD", "Ari", "Arb", "kuk", "kuc", "sha", "kuE", "shD", "kuC", "kuB", "Amk", "kdg", "Bqk", "kvg", "kda", "Bqc", "kva", "BqE",
"kvD", "BqC", "AmB", "BqB", "Ang", "kdr", "Brg", "kvr", "Bra", "AnD", "BrD", "Anr", "Brr", "sxc", "sxE", "sxC", "sxB", "ktc", "lvc", "sxq", "sgn", "lvE", "sxn", "lvC", "ktB", "lvB", "Alc",
"Bnc", "AlE", "kcn", "Drc", "BnE", "AlC", "DrE", "BnC", "AlB", "DrC", "BnB", "Alq", "Bnq", "Aln", "Drq", "Bnn", "Drn", "wyo", "wym", "wyl", "swo", "txo", "wyv", "txm", "swl", "txl", "kso",
"sgf", "lto", "swv", "nvo", "ltm", "ksl", "nvm", "ltl", "nvl", "Ako", "kcf", "Blo", "ksv", "Dno", "Blm", "Akl", "bro", "Dnm", "Bll", "brm", "Dnl", "Akv", "Blv", "Dnv", "brv", "yze", "yzd",
"wye", "xyu", "wyd", "xyt", "swe", "twu", "swd", "vxu", "twt", "vxt", "kse", "lsu", "ksd", "ntu", "lst", "rvu", "ypk", "zew", "xdA", "yos", "zei", "xck", "yog", "zeb", "xcc", "yoa", "xcE",
"yoD", "xcC", "xhk", "yqw", "zfj", "utA", "xgs", "yqi", "usk", "xgg", "yqb", "usc", "xga", "usE", "xgD", "usC", "uxk", "xiw", "yrj", "ptA", "uws", "xii", "psk", "uwg", "xib", "psc", "uwa",
"psE", "uwD", "psC", "pxk", "uyw", "xjj", "ftA", "pws", "uyi", "fsk", "pwg", "uyb", "fsc", "pwa", "fsE", "pwD", "fxk", "pyw", "uzj", "fws", "pyi", "fwg", "pyb", "fwa", "fyw", "pzj", "fyi",
"fyb", "xFA", "yms", "zdi", "xEk", "ymg", "zdb", "xEc", "yma", "xEE", "ymD", "xEC", "xEB", "uhA", "xas", "yni", "ugk", "xag", "ynb", "ugc", "xaa", "ugE", "xaD", "ugC", "ugB", "oxA", "uis",
"xbi", "owk", "uig", "xbb", "owc", "uia", "owE", "uiD", "owC", "owB", "dxA", "oys", "uji", "dwk", "oyg", "ujb", "dwc", "oya", "dwE", "oyD", "dwC", "dys", "ozi", "dyg", "ozb", "dya", "dyD",
"dzi", "dzb", "xCk", "ylg", "zcr", "xCc", "yla", "xCE", "ylD", "xCC", "xCB", "uak", "xDg", "ylr", "uac", "xDa", "uaE", "xDD", "uaC", "uaB", "oik", "ubg", "xDr", "oic", "uba", "oiE", "ubD",
"oiC", "oiB", "cyk", "ojg", "ubr", "cyc", "oja", "cyE", "ojD", "cyC", "cyB", "czg", "ojr", "cza", "czD", "czr", "xBc", "ykq", "xBE", "ykn", "xBC", "xBB", "uDc", "xBq", "uDE", "xBn", "uDC",
"uDB", "obc", "uDq", "obE", "uDn", "obC", "obB", "cjc", "obq", "cjE", "obn", "cjC", "cjB", "cjq", "cjn", "xAo", "ykf", "xAm", "xAl", "uBo", "xAv", "uBm", "uBl", "oDo", "uBv", "oDm", "oDl",
"cbo", "oDv", "cbm", "cbl", "xAe", "xAd", "uAu", "uAt", "oBu", "oBt", "wpA", "yes", "zFi", "wok", "yeg", "zFb", "woc", "yea", "woE", "yeD", "woC", "woB", "thA", "wqs", "yfi", "tgk", "wqg",
"yfb", "tgc", "wqa", "tgE", "wqD", "tgC", "tgB", "mxA", "tis", "wri", "mwk", "tig", "wrb", "mwc", "tia", "mwE", "tiD", "mwC", "mwB", "FxA", "mys", "tji", "Fwk", "myg", "tjb", "Fwc", "mya",
"FwE", "myD", "FwC", "Fys", "mzi", "Fyg", "mzb", "Fya", "FyD", "Fzi", "Fzb", "yuk", "zhg", "hjs", "yuc", "zha", "hbw", "yuE", "zhD", "hDy", "yuC", "yuB", "wmk", "ydg", "zEr", "xqk", "wmc",
"zhr", "xqc", "yva", "ydD", "xqE", "wmC", "xqC", "wmB", "xqB", "tak", "wng", "ydr", "vik", "tac", "wna", "vic", "xra", "wnD", "viE", "taC", "viC", "taB", "viB", "mik", "tbg", "wnr", "qyk",
"mic", "tba", "qyc", "vja", "tbD", "qyE", "miC", "qyC", "miB", "qyB", "Eyk", "mjg", "tbr", "hyk", "Eyc", "mja", "hyc", "qza", "mjD", "hyE", "EyC", "hyC", "EyB", "Ezg", "mjr", "hzg", "Eza",
"hza", "EzD", "hzD", "Ezr", "ytc", "zgq", "grw", "ytE", "zgn", "gny", "ytC", "glz", "ytB", "wlc", "ycq", "xnc", "wlE", "ycn", "xnE", "ytn", "xnC", "wlB", "xnB", "tDc", "wlq", "vbc", "tDE",
"wln", "vbE", "xnn", "vbC", "tDB", "vbB", "mbc", "tDq", "qjc", "mbE", "tDn", "qjE", "vbn", "qjC", "mbB", "qjB", "Ejc", "mbq", "gzc", "EjE", "mbn", "gzE", "qjn", "gzC", "EjB", "gzB", "Ejq",
"gzq", "Ejn", "gzn", "yso", "zgf", "gfy", "ysm", "gdz", "ysl", "wko", "ycf", "xlo", "ysv", "xlm", "wkl", "xll", "tBo", "wkv", "vDo", "tBm", "vDm", "tBl", "vDl", "mDo", "tBv", "qbo", "vDv",
"qbm", "mDl", "qbl", "Ebo", "mDv", "gjo", "Ebm", "gjm", "Ebl", "gjl", "Ebv", "gjv", "yse", "gFz", "ysd", "wke", "xku", "wkd", "xkt", "tAu", "vBu", "tAt", "vBt", "mBu", "qDu", "mBt", "qDt",
"EDu", "gbu", "EDt", "gbt", "ysF", "wkF", "xkh", "tAh", "vAx", "mAx", "qBx", "wek", "yFg", "zCr", "wec", "yFa", "weE", "yFD", "weC", "weB", "sqk", "wfg", "yFr", "sqc", "wfa", "sqE", "wfD",
"sqC", "sqB", "lik", "srg", "wfr", "lic", "sra", "liE", "srD", "liC", "liB", "Cyk", "ljg", "srr", "Cyc", "lja", "CyE", "ljD", "CyC", "CyB", "Czg", "ljr", "Cza", "CzD", "Czr", "yhc", "zaq",
"arw", "yhE", "zan", "any", "yhC", "alz", "yhB", "wdc", "yEq", "wvc", "wdE", "yEn", "wvE", "yhn", "wvC", "wdB", "wvB", "snc", "wdq", "trc", "snE", "wdn", "trE", "wvn", "trC", "snB", "trB",
"lbc", "snq", "njc", "lbE", "snn", "njE", "trn", "njC", "lbB", "njB", "Cjc", "lbq", "azc", "CjE", "lbn", "azE", "njn", "azC", "CjB", "azB", "Cjq", "azq", "Cjn", "azn", "zio", "irs", "rfy",
"zim", "inw", "rdz", "zil", "ily", "ikz", "ygo", "zaf", "afy", "yxo", "ziv", "ivy", "adz", "yxm", "ygl", "itz", "yxl", "wco", "yEf", "wto", "wcm", "xvo", "yxv", "wcl", "xvm", "wtl", "xvl",
"slo", "wcv", "tno", "slm", "vro", "tnm", "sll", "vrm", "tnl", "vrl", "lDo", "slv", "nbo", "lDm", "rjo", "nbm", "lDl", "rjm", "nbl", "rjl", "Cbo", "lDv", "ajo", "Cbm", "izo", "ajm", "Cbl",
"izm", "ajl", "izl", "Cbv", "ajv", "zie", "ifw", "rFz", "zid", "idy", "icz", "yge", "aFz", "ywu", "ygd", "ihz", "ywt", "wce", "wsu", "wcd", "xtu", "wst", "xtt", "sku", "tlu", "skt", "vnu",
"tlt", "vnt", "lBu", "nDu", "lBt", "rbu", "nDt", "rbt", "CDu", "abu", "CDt", "iju", "abt", "ijt", "ziF", "iFy", "iEz", "ygF", "ywh", "wcF", "wsh", "xsx", "skh", "tkx", "vlx", "lAx", "nBx",
"rDx", "CBx", "aDx", "ibx", "iCz", "wFc", "yCq", "wFE", "yCn", "wFC", "wFB", "sfc", "wFq", "sfE", "wFn", "sfC", "sfB", "krc", "sfq", "krE", "sfn", "krC", "krB", "Bjc", "krq", "BjE", "krn",
"BjC", "BjB", "Bjq", "Bjn", "yao", "zDf", "Dfy", "yam", "Ddz", "yal", "wEo", "yCf", "who", "wEm", "whm", "wEl", "whl", "sdo", "wEv", "svo", "sdm", "svm", "sdl", "svl", "kno", "sdv", "lro",
"knm", "lrm", "knl", "lrl", "Bbo", "knv", "Djo", "Bbm", "Djm", "Bbl", "Djl", "Bbv", "Djv", "zbe", "bfw", "npz", "zbd", "bdy", "bcz", "yae", "DFz", "yiu", "yad", "bhz", "yit", "wEe", "wgu",
"wEd", "wxu", "wgt", "wxt", "scu", "stu", "sct", "tvu", "stt", "tvt", "klu", "lnu", "klt", "nru", "lnt", "nrt", "BDu", "Dbu", "BDt", "bju", "Dbt", "bjt", "jfs", "rpy", "jdw", "roz", "jcy",
"jcj", "zbF", "bFy", "zjh", "jhy", "bEz", "jgz", "yaF", "yih", "yyx", "wEF", "wgh", "wwx", "xxx", "sch", "ssx", "ttx", "vvx", "kkx", "llx", "nnx", "rrx", "BBx", "DDx", "bbx", "jFw", "rmz",
"jEy", "jEj", "bCz", "jaz", "jCy", "jCj", "jBj", "wCo", "wCm", "wCl", "sFo", "wCv", "sFm", "sFl", "kfo", "sFv", "kfm", "kfl", "Aro", "kfv", "Arm", "Arl", "Arv", "yDe", "Bpz", "yDd", "wCe",
"wau", "wCd", "wat", "sEu", "shu", "sEt", "sht", "kdu", "kvu", "kdt", "kvt", "Anu", "Bru", "Ant", "Brt", "zDp", "Dpy", "Doz", "yDF", "ybh", "wCF", "wah", "wix", "sEh", "sgx", "sxx", "kcx",
"ktx", "lvx", "Alx", "Bnx", "Drx", "bpw", "nuz", "boy", "boj", "Dmz", "bqz", "jps", "ruy", "jow", "ruj", "joi", "job", "bmy", "jqy", "bmj", "jqj", "jmw", "rtj", "jmi", "jmb", "blj", "jnj",
"jli", "jlb", "jkr", "sCu", "sCt", "kFu", "kFt", "Afu", "Aft", "wDh", "sCh", "sax", "kEx", "khx", "Adx", "Avx", "Buz", "Duy", "Duj", "buw", "nxj", "bui", "bub", "Dtj", "bvj", "jus", "rxi",
"jug", "rxb", "jua", "juD", "bti", "jvi", "btb", "jvb", "jtg", "rwr", "jta", "jtD", "bsr", "jtr", "jsq", "jsn", "Bxj", "Dxi", "Dxb", "bxg", "nyr", "bxa", "bxD", "Dwr", "bxr", "bwq", "bwn",
"pjk", "urw", "ejA", "pbs", "uny", "ebk", "pDw", "ulz", "eDs", "pBy", "eBw", "zfc", "fjk", "prw", "zfE", "fbs", "pny", "zfC", "fDw", "plz", "zfB", "fBy", "yrc", "zfq", "frw", "yrE", "zfn",
"fny", "yrC", "flz", "yrB", "xjc", "yrq", "xjE", "yrn", "xjC", "xjB", "uzc", "xjq", "uzE", "xjn", "uzC", "uzB", "pzc", "uzq", "pzE", "uzn", "pzC", "djA", "ors", "ufy", "dbk", "onw", "udz",
"dDs", "oly", "dBw", "okz", "dAy", "zdo", "drs", "ovy", "zdm", "dnw", "otz", "zdl", "dly", "dkz", "yno", "zdv", "dvy", "ynm", "dtz", "ynl", "xbo", "ynv", "xbm", "xbl", "ujo", "xbv", "ujm",
"ujl", "ozo", "ujv", "ozm", "ozl", "crk", "ofw", "uFz", "cns", "ody", "clw", "ocz", "cky", "ckj", "zcu", "cvw", "ohz", "zct", "cty", "csz", "ylu", "cxz", "ylt", "xDu", "xDt", "ubu", "ubt",
"oju", "ojt", "cfs", "oFy", "cdw", "oEz", "ccy", "ccj", "zch", "chy", "cgz", "ykx", "xBx", "uDx", "cFw", "oCz", "cEy", "cEj", "caz", "cCy", "cCj", "FjA", "mrs", "tfy", "Fbk", "mnw", "tdz",
"FDs", "mly", "FBw", "mkz", "FAy", "zFo", "Frs", "mvy", "zFm", "Fnw", "mtz", "zFl", "Fly", "Fkz", "yfo", "zFv", "Fvy", "yfm", "Ftz", "yfl", "wro", "yfv", "wrm", "wrl", "tjo", "wrv", "tjm",
"tjl", "mzo", "tjv", "mzm", "mzl", "qrk", "vfw", "xpz", "hbA", "qns", "vdy", "hDk", "qlw", "vcz", "hBs", "qky", "hAw", "qkj", "hAi", "Erk", "mfw", "tFz", "hrk", "Ens", "mdy", "hns", "qty",
"mcz", "hlw", "Eky", "hky", "Ekj", "hkj", "zEu", "Evw", "mhz", "zhu", "zEt", "hvw", "Ety", "zht", "hty", "Esz", "hsz", "ydu", "Exz", "yvu", "ydt", "hxz", "yvt", "wnu", "xru", "wnt", "xrt",
"tbu", "vju", "tbt", "vjt", "mju", "mjt", "grA", "qfs", "vFy", "gnk", "qdw", "vEz", "gls", "qcy", "gkw", "qcj", "gki", "gkb", "Efs", "mFy", "gvs", "Edw", "mEz", "gtw", "qgz", "gsy", "Ecj",
"gsj", "zEh", "Ehy", "zgx", "gxy", "Egz", "gwz", "ycx", "ytx", "wlx", "xnx", "tDx", "vbx", "mbx", "gfk", "qFw", "vCz", "gds", "qEy", "gcw", "qEj", "gci", "gcb", "EFw", "mCz", "ghw", "EEy",
"ggy", "EEj", "ggj", "Eaz", "giz", "gFs", "qCy", "gEw", "qCj", "gEi", "gEb", "ECy", "gay", "ECj", "gaj", "gCw", "qBj", "gCi", "gCb", "EBj", "gDj", "gBi", "gBb", "Crk", "lfw", "spz", "Cns",
"ldy", "Clw", "lcz", "Cky", "Ckj", "zCu", "Cvw", "lhz", "zCt", "Cty", "Csz", "yFu", "Cxz", "yFt", "wfu", "wft", "sru", "srt", "lju", "ljt", "arA", "nfs", "tpy", "ank", "ndw", "toz", "als",
"ncy", "akw", "ncj", "aki", "akb", "Cfs", "lFy", "avs", "Cdw", "lEz", "atw", "ngz", "asy", "Ccj", "asj", "zCh", "Chy", "zax", "axy", "Cgz", "awz", "yEx", "yhx", "wdx", "wvx", "snx", "trx",
"lbx", "rfk", "vpw", "xuz", "inA", "rds", "voy", "ilk", "rcw", "voj", "iks", "rci", "ikg", "rcb", "ika", "afk", "nFw", "tmz", "ivk", "ads", "nEy", "its", "rgy", "nEj", "isw", "aci", "isi",
"acb", "isb", "CFw", "lCz", "ahw", "CEy", "ixw", "agy", "CEj", "iwy", "agj", "iwj", "Caz", "aiz", "iyz", "ifA", "rFs", "vmy", "idk", "rEw", "vmj", "ics", "rEi", "icg", "rEb", "ica", "icD",
"aFs", "nCy", "ihs", "aEw", "nCj", "igw", "raj", "igi", "aEb", "igb", "CCy", "aay", "CCj", "iiy", "aaj", "iij", "iFk", "rCw", "vlj", "iEs", "rCi", "iEg", "rCb", "iEa", "iED", "aCw", "nBj",
"iaw", "aCi", "iai", "aCb", "iab", "CBj", "aDj", "ibj", "iCs", "rBi", "iCg", "rBb", "iCa", "iCD", "aBi", "iDi", "aBb", "iDb", "iBg", "rAr", "iBa", "iBD", "aAr", "iBr", "iAq", "iAn", "Bfs",
"kpy", "Bdw", "koz", "Bcy", "Bcj", "Bhy", "Bgz", "yCx", "wFx", "sfx", "krx", "Dfk", "lpw", "suz", "Dds", "loy", "Dcw", "loj", "Dci", "Dcb", "BFw", "kmz", "Dhw", "BEy", "Dgy", "BEj", "Dgj",
"Baz", "Diz", "bfA", "nps", "tuy", "bdk", "now", "tuj", "bcs", "noi", "bcg", "nob", "bca", "bcD", "DFs", "lmy", "bhs", "DEw", "lmj", "bgw", "DEi", "bgi", "DEb", "bgb", "BCy", "Day", "BCj",
"biy", "Daj", "bij", "rpk", "vuw", "xxj", "jdA", "ros", "vui", "jck", "rog", "vub", "jcc", "roa", "jcE", "roD", "jcC", "bFk", "nmw", "ttj", "jhk", "bEs", "nmi", "jgs", "rqi", "nmb", "jgg",
"bEa", "jga", "bED", "jgD", "DCw", "llj", "baw", "DCi", "jiw", "bai", "DCb", "jii", "bab", "jib", "BBj", "DDj", "bbj", "jjj", "jFA", "rms", "vti", "jEk", "rmg", "vtb", "jEc", "rma", "jEE",
"rmD", "jEC", "jEB", "bCs", "nli", "jas", "bCg", "nlb", "jag", "rnb", "jaa", "bCD", "jaD", "DBi", "bDi", "DBb", "jbi", "bDb", "jbb", "jCk", "rlg", "vsr", "jCc", "rla", "jCE", "rlD", "jCC",
"jCB", "bBg", "nkr", "jDg", "bBa", "jDa", "bBD", "jDD", "DAr", "bBr", "jDr", "jBc", "rkq", "jBE", "rkn", "jBC", "jBB", "bAq", "jBq", "bAn", "jBn", "jAo", "rkf", "jAm", "jAl", "bAf", "jAv",
"Apw", "kez", "Aoy", "Aoj", "Aqz", "Bps", "kuy", "Bow", "kuj", "Boi", "Bob", "Amy", "Bqy", "Amj", "Bqj", "Dpk", "luw", "sxj", "Dos", "lui", "Dog", "lub", "Doa", "DoD", "Bmw", "ktj", "Dqw",
"Bmi", "Dqi", "Bmb", "Dqb", "Alj", "Bnj", "Drj", "bpA", "nus", "txi", "bok", "nug", "txb", "boc", "nua", "boE", "nuD", "boC", "boB", "Dms", "lti", "bqs", "Dmg", "ltb", "bqg", "nvb", "bqa",
"DmD", "bqD", "Bli", "Dni", "Blb", "bri", "Dnb", "brb", "ruk", "vxg", "xyr", "ruc", "vxa", "ruE", "vxD", "ruC", "ruB", "bmk", "ntg", "twr", "jqk", "bmc", "nta", "jqc", "rva", "ntD", "jqE",
"bmC", "jqC", "bmB", "jqB", "Dlg", "lsr", "bng", "Dla", "jrg", "bna", "DlD", "jra", "bnD", "jrD", "Bkr", "Dlr", "bnr", "jrr", "rtc", "vwq", "rtE", "vwn", "rtC", "rtB", "blc", "nsq", "jnc",
"blE", "nsn", "jnE", "rtn", "jnC", "blB", "jnB", "Dkq", "blq", "Dkn", "jnq", "bln", "jnn", "rso", "vwf", "rsm", "rsl", "bko", "nsf", "jlo", "bkm", "jlm", "bkl", "jll", "Dkf", "bkv", "jlv",
"rse", "rsd", "bke", "jku", "bkd", "jkt", "Aey", "Aej", "Auw", "khj", "Aui", "Aub", "Adj", "Avj", "Bus", "kxi", "Bug", "kxb", "Bua", "BuD", "Ati", "Bvi", "Atb", "Bvb", "Duk", "lxg", "syr",
"Duc", "lxa", "DuE", "lxD", "DuC", "DuB", "Btg", "kwr", "Dvg", "lxr", "Dva", "BtD", "DvD", "Asr", "Btr", "Dvr", "nxc", "tyq", "nxE", "tyn", "nxC", "nxB", "Dtc", "lwq", "bvc", "nxq", "lwn",
"bvE", "DtC", "bvC", "DtB", "bvB", "Bsq", "Dtq", "Bsn", "bvq", "Dtn", "bvn", "vyo", "xzf", "vym", "vyl", "nwo", "tyf", "rxo", "nwm", "rxm", "nwl", "rxl", "Dso", "lwf", "bto", "Dsm", "jvo",
"btm", "Dsl", "jvm", "btl", "jvl", "Bsf", "Dsv", "btv", "jvv", "vye", "vyd", "nwe", "rwu", "nwd", "rwt", "Dse", "bsu", "Dsd", "jtu", "bst", "jtt", "vyF", "nwF", "rwh", "DsF", "bsh", "jsx",
"Ahi", "Ahb", "Axg", "kir", "Axa", "AxD", "Agr", "Axr", "Bxc", "kyq", "BxE", "kyn", "BxC", "BxB", "Awq", "Bxq", "Awn", "Bxn", "lyo", "szf", "lym", "lyl", "Bwo", "kyf", "Dxo", "lyv", "Dxm",
"Bwl", "Dxl", "Awf", "Bwv", "Dxv", "tze", "tzd", "lye", "nyu", "lyd", "nyt", "Bwe", "Dwu", "Bwd", "bxu", "Dwt", "bxt", "tzF", "lyF", "nyh", "BwF", "Dwh", "bwx", "Aiq", "Ain", "Ayo", "kjf",
"Aym", "Ayl", "Aif", "Ayv", "kze", "kzd", "Aye", "Byu", "Ayd", "Byt", "szp" };
 
private static final char[] BR_SET = { 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
'z', '*', '+', '-' };
 
private static final String[] PDF_TTF = { "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000",
"10001", "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010", "11011", "11100", "11101", "11110", "11111", "01", "1111111101010100", "11111101000101001" };
 
private static final int[] ASCII_X = { 7, 8, 8, 4, 12, 4, 4, 8, 8, 8, 12, 4, 12, 12, 12, 12, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12, 8, 8, 4, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 8, 8, 4, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 8, 8, 8 };
 
private static final int[] ASCII_Y = { 26, 10, 20, 15, 18, 21, 10, 28, 23, 24, 22, 20, 13, 16, 17, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 0, 1, 23, 2, 25, 3, 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, 4, 5, 6, 24, 7, 8, 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, 21, 27, 9 };
 
private static final int[] MICRO_AUTOSIZE = { 4, 6, 7, 8, 8, 10, 10, 12, 12, 13, 14, 16, 18, 18, 19, 20, 24, 24, 24, 29, 30, 33, 34, 37, 39, 46, 54, 58, 70, 72, 82, 90, 108, 126, // max
// codeword
// counts
1, 14, 2, 7, 24, 3, 15, 25, 4, 8, 16, 5, 17, 26, 9, 6, 10, 18, 27, 11, 28, 12, 19, 13, 29, 20, 30, 21, 22, 31, 23, 32, 33, 34 // corresponding
// variant
};
 
/*
* Rows, columns, error codewords, k-offset of valid MicroPDF417 sizes from ISO/IEC 24728:2006
*/
private static final int[] MICRO_VARIANTS = { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // columns
11, 14, 17, 20, 24, 28, 8, 11, 14, 17, 20, 23, 26, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 4, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, // rows
7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 11, 13, 15, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, 8, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, // k
// (EC
// codewords)
0, 0, 0, 7, 7, 7, 7, 15, 15, 24, 34, 57, 84, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294, 7, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294 // offset
};
 
/*
* Following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24728:2006 tables
* 10, 11 and 12
*/
private static final int[] RAP_TABLE = { 1, 8, 36, 19, 9, 25, 1, 1, 8, 36, 19, 9, 27, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 47, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, // left
// RAP
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 19, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, // centre
// RAP
9, 8, 36, 19, 17, 33, 1, 9, 8, 36, 19, 17, 35, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, 43, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, // right
// RAP
0, 3, 6, 0, 6, 0, 0, 0, 3, 6, 0, 6, 6, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 3, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0 // start
// cluster
};
 
/* Left and Right Row Address Pattern from Table 2 */
private static final String[] RAPLR = { "", "221311", "311311", "312211", "222211", "213211", "214111", "223111", "313111", "322111", "412111", "421111", "331111", "241111", "232111", "231211",
"321211", "411211", "411121", "411112", "321112", "312112", "311212", "311221", "311131", "311122", "311113", "221113", "221122", "221131", "221221", "222121", "312121", "321121",
"231121", "231112", "222112", "213112", "212212", "212221", "212131", "212122", "212113", "211213", "211123", "211132", "211141", "211231", "211222", "211312", "211321", "211411",
"212311" };
 
/* Centre Row Address Pattern from Table 2 */
private static final String[] RAPC = { "", "112231", "121231", "122131", "131131", "131221", "132121", "141121", "141211", "142111", "133111", "132211", "131311", "122311", "123211", "124111",
"115111", "114211", "114121", "123121", "123112", "122212", "122221", "121321", "121411", "112411", "113311", "113221", "113212", "113122", "122122", "131122", "131113", "122113",
"113113", "112213", "112222", "112312", "112321", "111421", "111331", "111322", "111232", "111223", "111133", "111124", "111214", "112114", "121114", "121123", "121132", "112132",
"112141" };
 
/* MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F */
private static final int[] MICRO_COEFFS = {
/* k = 7 */
76, 925, 537, 597, 784, 691, 437,
 
/* k = 8 */
237, 308, 436, 284, 646, 653, 428, 379,
 
/* k = 9 */
567, 527, 622, 257, 289, 362, 501, 441, 205,
 
/* k = 10 */
377, 457, 64, 244, 826, 841, 818, 691, 266, 612,
 
/* k = 11 */
462, 45, 565, 708, 825, 213, 15, 68, 327, 602, 904,
 
/* k = 12 */
597, 864, 757, 201, 646, 684, 347, 127, 388, 7, 69, 851,
 
/* k = 13 */
764, 713, 342, 384, 606, 583, 322, 592, 678, 204, 184, 394, 692,
 
/* k = 14 */
669, 677, 154, 187, 241, 286, 274, 354, 478, 915, 691, 833, 105, 215,
 
/* k = 15 */
460, 829, 476, 109, 904, 664, 230, 5, 80, 74, 550, 575, 147, 868, 642,
 
/* k = 16 */
274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65,
 
/* k = 18 */
279, 577, 315, 624, 37, 855, 275, 739, 120, 297, 312, 202, 560, 321, 233, 756, 760, 573,
 
/* k = 21 */
108, 519, 781, 534, 129, 425, 681, 553, 422, 716, 763, 693, 624, 610, 310, 691, 347, 165, 193, 259, 568,
 
/* k = 26 */
443, 284, 887, 544, 788, 93, 477, 760, 331, 608, 269, 121, 159, 830, 446, 893, 699, 245, 441, 454, 325, 858, 131, 847, 764, 169,
 
/* k = 32 */
361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410,
 
/* k = 38 */
234, 228, 438, 848, 133, 703, 529, 721, 788, 322, 280, 159, 738, 586, 388, 684, 445, 680, 245, 595, 614, 233, 812, 32, 284, 658, 745, 229, 95, 689, 920, 771, 554, 289, 231, 125, 117, 518,
 
/* k = 44 */
476, 36, 659, 848, 678, 64, 764, 840, 157, 915, 470, 876, 109, 25, 632, 405, 417, 436, 714, 60, 376, 97, 413, 706, 446, 21, 3, 773, 569, 267, 272, 213, 31, 560, 231, 758, 103, 271, 572,
436, 339, 730, 82, 285,
 
/* k = 50 */
923, 797, 576, 875, 156, 706, 63, 81, 257, 874, 411, 416, 778, 50, 205, 303, 188, 535, 909, 155, 637, 230, 534, 96, 575, 102, 264, 233, 919, 593, 865, 26, 579, 623, 766, 146, 10, 739, 246,
127, 71, 244, 211, 477, 920, 876, 427, 820, 718, 435 };
 
/**
* Creates a new PDF417 symbol instance.
*/
public Pdf417() {
setBarHeight(3);
}
 
/**
* Sets the default bar height (height of a single row) for this symbol (default value is
* <code>3</code>).
*
* @param barHeight the default bar height for this symbol
*/
@Override
public void setBarHeight(final int barHeight) {
super.setBarHeight(barHeight);
}
 
/**
* Sets the width of the symbol by specifying the number of columns of data codewords. Valid
* values are 1-30 for PDF417 and 1-4 for MicroPDF417.
*
* @param columns the number of data columns in the symbol
*/
public void setDataColumns(final int columns) {
this.columns = columns;
}
 
/**
* Returns the number of data columns used by this symbol, or {@code null} if the number of data
* columns has not been set.
*
* @return the number of data columns used by this symbol
*/
public Integer getDataColumns() {
return this.columns;
}
 
/**
* Sets the height of the symbol by specifying the number of rows of data codewords. Valid
* values are 3-90 for PDF417 and 4-44 for MicroPDF417.
*
* @param rows the number of rows in the symbol
*/
public void setRows(final int rows) {
this.rows = rows;
}
 
/**
* Returns the number of rows used by this symbol, or {@code null} if the number of rows has not
* been set.
*
* @return the number of rows used by this symbol
*/
public Integer getRows() {
return this.rows;
}
 
/**
* Set the amount of the symbol which is dedicated to error correction codewords. The number of
* codewords of error correction data is determined by 2<sup>(eccLevel + 1)</sup>. This
* attribute is ignored when using {@link Mode#MICRO micro} mode.
*
* @param eccLevel level of error correction (0-8)
*/
public void setPreferredEccLevel(final int eccLevel) {
if (eccLevel < 0 || eccLevel > 8) {
throw new IllegalArgumentException("ECC level must be between 0 and 8.");
}
this.preferredEccLevel = eccLevel;
}
 
/**
* Returns the preferred error correction level.
*
* @return the preferred error correction level
*/
public int getPreferredEccLevel() {
return this.preferredEccLevel;
}
 
/**
* Forces the use of the specified MicroPDF417 variant. Only valid when using {@link Mode#MICRO
* micro} mode.
*
* @param variant the MicroPDF417 variant to use
*/
public void setVariant(final int variant) {
if (this.symbolMode != Mode.MICRO) {
throw new IllegalArgumentException("Can only set variant when using MICRO mode.");
}
if (variant < 1 || variant > 34) {
throw new IllegalArgumentException("Variant must be between 1 and 34.");
}
this.columns = MICRO_VARIANTS[variant - 1];
this.rows = MICRO_VARIANTS[variant - 1 + 34];
}
 
/**
* If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format
* (Macro PDF417), this method sets the position of this symbol in the series. Valid values are
* 1 through 99,999 inclusive.
*
* @param position the position of this PDF417 symbol in the structured append series
*/
public void setStructuredAppendPosition(final int position) {
if (position < 1 || position > 99_999) {
throw new IllegalArgumentException("Invalid PDF417 structured append position: " + position);
}
this.structuredAppendPosition = position;
}
 
/**
* Returns the position of this PDF417 symbol in a series of symbols using structured append
* (Macro PDF417). If this symbol is not part of such a series, this method will return
* <code>1</code>.
*
* @return the position of this PDF417 symbol in a series of symbols using structured append
*/
public int getStructuredAppendPosition() {
return this.structuredAppendPosition;
}
 
/**
* If this PDF417 symbol is part of a series of PDF417 symbols appended in a structured format
* (Macro PDF417), this method sets the total number of symbols in the series. Valid values are
* 1 through 99,999 inclusive. A value of 1 indicates that this symbol is not part of a
* structured append series.
*
* @param total the total number of PDF417 symbols in the structured append series
*/
public void setStructuredAppendTotal(final int total) {
if (total < 1 || total > 99_999) {
throw new IllegalArgumentException("Invalid PDF417 structured append total: " + total);
}
this.structuredAppendTotal = total;
}
 
/**
* Returns the size of the series of PDF417 symbols using structured append (Macro PDF417) 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 PDF417 symbol is part of a series of PDF417 symbols appended in a structured format
* (Macro PDF417), this method sets the unique file ID for the series. Valid values are 0
* through 899 inclusive.
*
* @param fileId the unique file ID for the series that this symbol is part of
*/
public void setStructuredAppendFileId(final int fileId) {
if (fileId < 0 || fileId > 899) {
throw new IllegalArgumentException("Invalid PDF417 structured append file ID: " + fileId);
}
this.structuredAppendFileId = fileId;
}
 
/**
* Returns the unique file ID of the series of PDF417 symbols using structured append (Macro
* PDF417) that this symbol is part of. If this symbol is not part of a structured append
* series, this method will return <code>0</code>.
*
* @return the unique file ID for the series that this symbol is part of
*/
public int getStructuredAppendFileId() {
return this.structuredAppendFileId;
}
 
public void setMode(final Mode mode) {
this.symbolMode = mode;
}
 
public Mode getMode() {
return this.symbolMode;
}
 
@Override
protected void encode() {
 
eciProcess();
 
switch (this.symbolMode) {
case MICRO:
processMicroPdf417();
break;
case NORMAL:
case TRUNCATED:
default:
processPdf417();
break;
}
}
 
private void processPdf417() {
int j, loop, offset;
final int[] mccorrection = new int[520];
int total;
int c1, c2, c3;
final int[] dummy = new int[35];
int selectedECCLevel;
final StringBuilder codebarre = new StringBuilder();
final StringBuilder bin = new StringBuilder();
 
final List<Block> blocks = createBlocks(this.inputData);
 
/* now compress the data */
this.codeWordCount = 0;
 
if (this.readerInit) {
this.codeWords[this.codeWordCount] = 921; /* Reader Initialisation */
this.codeWordCount++;
}
 
if (this.eciMode != 3) {
/* Encoding ECI assignment number, from ISO/IEC 15438 Table 8 */
if (this.eciMode <= 899) {
this.codeWords[this.codeWordCount] = 927;
this.codeWordCount++;
this.codeWords[this.codeWordCount] = this.eciMode;
this.codeWordCount++;
}
 
if (this.eciMode >= 900 && this.eciMode <= 810899) {
this.codeWords[this.codeWordCount] = 926;
this.codeWordCount++;
this.codeWords[this.codeWordCount] = this.eciMode / 900 - 1;
this.codeWordCount++;
this.codeWords[this.codeWordCount] = this.eciMode % 900;
this.codeWordCount++;
}
 
if (this.eciMode >= 810900 && this.eciMode <= 811799) {
this.codeWords[this.codeWordCount] = 925;
this.codeWordCount++;
this.codeWords[this.codeWordCount] = this.eciMode - 810900;
this.codeWordCount++;
}
}
 
int blockCount = 0;
for (int i = 0; i < blocks.size(); i++) {
final Block block = blocks.get(i);
switch (block.mode) {
case TEX:
/* text mode */
final boolean firstBlock = i == 0;
processText(blockCount, block.length, firstBlock);
break;
case BYT:
/* octet stream mode */
final EncodingMode lastMode = i == 0 ? EncodingMode.TEX : blocks.get(i - 1).mode;
processBytes(blockCount, block.length, lastMode);
break;
case NUM:
/* numeric mode */
processNumbers(this.inputData, blockCount, block.length, false);
break;
default:
throw new OkapiException("Unknown block type: " + block.mode);
}
blockCount += block.length;
}
 
addMacroCodewords();
 
info("Codewords: ");
for (int i = 0; i < this.codeWordCount; i++) {
infoSpace(this.codeWords[i]);
}
infoLine();
 
/* Now take care of the number of CWs per row */
 
// if we have to default the ECC level, do so per the
// recommendations in the specification (Table E.1)
selectedECCLevel = this.preferredEccLevel;
if (selectedECCLevel < 0) {
if (this.codeWordCount <= 40) {
selectedECCLevel = 2;
} else if (this.codeWordCount <= 160) {
selectedECCLevel = 3;
} else if (this.codeWordCount <= 320) {
selectedECCLevel = 4;
} else if (this.codeWordCount <= 863) {
selectedECCLevel = 5;
} else {
selectedECCLevel = 6;
}
}
 
int k = 1 << selectedECCLevel + 1; // error correction codeword count
final int dataCodeWordCount = this.codeWordCount + k + 1; // not including padding
 
validateRows(3, 90);
validateColumns(1, 30);
 
if (this.columns != null) {
if (this.rows != null) {
// user specified both columns and rows; make sure the data fits
if (this.columns * this.rows < dataCodeWordCount) {
throw new OkapiException("Too few rows (" + this.rows + ") and columns (" + this.columns + ") to hold codewords (" + dataCodeWordCount + ")");
}
} else {
// user only specified column count; figure out row count
this.rows = (int) Math.ceil(dataCodeWordCount / (double) this.columns);
}
} else {
if (this.rows != null) {
// user only specified row count; figure out column count
this.columns = (int) Math.ceil(dataCodeWordCount / (double) this.rows);
} else {
// user didn't specify columns or rows; figure both out
this.columns = (int) (0.5 + Math.sqrt((dataCodeWordCount - 1) / 3.0));
this.rows = (int) Math.ceil(dataCodeWordCount / (double) this.columns);
}
}
 
validateRows(3, 90);
validateColumns(1, 30);
 
/* add the padding */
int paddingCount = this.columns * this.rows - this.codeWordCount - k - 1;
while (paddingCount > 0) {
this.codeWords[this.codeWordCount] = 900;
this.codeWordCount++;
paddingCount--;
}
 
/* add the length descriptor */
for (int i = this.codeWordCount; i > 0; i--) {
this.codeWords[i] = this.codeWords[i - 1];
}
this.codeWordCount++;
this.codeWords[0] = this.codeWordCount;
 
/* 796 - we now take care of the Reed Solomon codes */
switch (selectedECCLevel) {
case 1:
offset = 2;
break;
case 2:
offset = 6;
break;
case 3:
offset = 14;
break;
case 4:
offset = 30;
break;
case 5:
offset = 62;
break;
case 6:
offset = 126;
break;
case 7:
offset = 254;
break;
case 8:
offset = 510;
break;
default:
offset = 0;
break;
}
 
for (loop = 0; loop < 520; loop++) {
mccorrection[loop] = 0;
}
 
for (int i = 0; i < this.codeWordCount; i++) {
total = (this.codeWords[i] + mccorrection[k - 1]) % 929;
for (j = k - 1; j > 0; j--) {
mccorrection[j] = (mccorrection[j - 1] + 929 - total * COEFRS[offset + j] % 929) % 929;
}
mccorrection[0] = (929 - total * COEFRS[offset + j] % 929) % 929;
}
 
infoLine("Data Codewords: " + this.codeWordCount);
infoLine("ECC Codewords: " + k);
 
/* we add these codes to the string */
for (int i = k - 1; i >= 0; i--) {
this.codeWords[this.codeWordCount++] = mccorrection[i] != 0 ? 929 - mccorrection[i] : 0;
}
 
/* make sure total codeword count isn't too high */
if (this.codeWordCount > 929) {
throw new OkapiException("Too many codewords required (" + this.codeWordCount + ", but max is 929)");
}
 
/* 818 - The CW string is finished */
c1 = (this.rows - 1) / 3;
c2 = selectedECCLevel * 3 + (this.rows - 1) % 3;
c3 = this.columns - 1;
 
this.readable = "";
this.row_count = this.rows;
this.pattern = new String[this.rows];
this.row_height = new int[this.rows];
infoLine("Grid Size: " + this.columns + " X " + this.rows);
 
/* we now encode each row */
for (int i = 0; i < this.rows; i++) {
for (j = 0; j < this.columns; j++) {
dummy[j + 1] = this.codeWords[i * this.columns + j];
}
k = i / 3 * 30;
switch (i % 3) {
case 0:
offset = 0; // cluster 0
dummy[0] = k + c1; // left row indicator
dummy[this.columns + 1] = k + c3; // right row indicator
break;
case 1:
offset = 929; // cluster 3
dummy[0] = k + c2; // left row indicator
dummy[this.columns + 1] = k + c1; // right row indicator
break;
case 2:
offset = 1858; // cluster 6
dummy[0] = k + c3; // left row indicator
dummy[this.columns + 1] = k + c2; // right row indicator
break;
}
codebarre.setLength(0);
codebarre.append("+*");
for (j = 0; j <= this.columns + 1; j++) {
if (!(this.symbolMode == Mode.TRUNCATED && j > this.columns)) {
codebarre.append(CODAGEMC[offset + dummy[j]]);
codebarre.append('*');
}
}
if (this.symbolMode != Mode.TRUNCATED) {
codebarre.append('-');
}
bin.setLength(0);
for (j = 0; j < codebarre.length(); j++) {
bin.append(PDF_TTF[positionOf(codebarre.charAt(j), BR_SET)]);
}
this.pattern[i] = bin2pat(bin);
this.row_height[i] = this.default_height;
}
}
 
private void processMicroPdf417() { /* like PDF417 only much smaller! */
 
int k, j, longueur, offset;
int total;
int LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster;
int LeftRAP, CentreRAP, RightRAP, Cluster, flip, loop;
final int[] dummy = new int[5];
final int[] mccorrection = new int[50];
final StringBuilder codebarre = new StringBuilder();
final StringBuilder bin = new StringBuilder();
 
/* Encoding starts out the same as PDF417, so use the same code */
 
final List<Block> blocks = createBlocks(this.inputData);
 
/* 541 - now compress the data */
this.codeWordCount = 0;
if (this.readerInit) {
this.codeWords[this.codeWordCount] = 921; /* Reader Initialisation */
this.codeWordCount++;
}
 
if (this.eciMode != 3) {
/* Encoding ECI assignment number, from ISO/IEC 15438 Table 8 */
if (this.eciMode <= 899) {
this.codeWords[this.codeWordCount] = 927;
this.codeWordCount++;
this.codeWords[this.codeWordCount] = this.eciMode;
this.codeWordCount++;
}
 
if (this.eciMode >= 900 && this.eciMode <= 810899) {
this.codeWords[this.codeWordCount] = 926;
this.codeWordCount++;
this.codeWords[this.codeWordCount] = this.eciMode / 900 - 1;
this.codeWordCount++;
this.codeWords[this.codeWordCount] = this.eciMode % 900;
this.codeWordCount++;
}
 
if (this.eciMode >= 810900 && this.eciMode <= 811799) {
this.codeWords[this.codeWordCount] = 925;
this.codeWordCount++;
this.codeWords[this.codeWordCount] = this.eciMode - 810900;
this.codeWordCount++;
}
}
 
int blockCount = 0;
for (int i = 0; i < blocks.size(); i++) {
final Block block = blocks.get(i);
switch (block.mode) {
case TEX:
/* text mode */
processText(blockCount, block.length, false); // TODO: this shouldn't always be
// false?
break;
case BYT:
/* octet stream mode */
final EncodingMode lastMode = i == 0 ? EncodingMode.TEX : blocks.get(i - 1).mode;
processBytes(blockCount, block.length, lastMode);
break;
case NUM:
/* numeric mode */
processNumbers(this.inputData, blockCount, block.length, false);
break;
default:
throw new OkapiException("Unknown block type: " + block.mode);
}
blockCount += block.length;
}
 
addMacroCodewords();
 
info("Codewords: ");
for (int i = 0; i < this.codeWordCount; i++) {
infoSpace(this.codeWords[i]);
}
infoLine();
 
/* This is where it all changes! */
 
validateRows(4, 44);
validateColumns(1, 4);
 
if (this.columns != null) {
int max;
switch (this.columns) {
case 1:
max = 20;
break;
case 2:
max = 37;
break;
case 3:
max = 82;
break;
case 4:
max = 126;
break;
default:
throw new OkapiException("Invalid column count: " + this.columns);
}
if (this.codeWordCount > max) {
throw new OkapiException("Too few columns (" + this.columns + ") to hold data codewords (" + this.codeWordCount + ")");
}
}
 
/* Now figure out which variant of the symbol to use and load values accordingly */
 
int variant = getMicroPdf417Variant(this.codeWordCount, this.columns, this.rows);
 
/* Now we have the variant we can load the data */
 
variant--;
this.columns = MICRO_VARIANTS[variant]; /* columns */
this.rows = MICRO_VARIANTS[variant + 34]; /* rows */
k = MICRO_VARIANTS[variant + 68]; /* number of EC CWs */
longueur = this.columns * this.rows - k; /* number of non-EC CWs */
int padding = longueur - this.codeWordCount; /* amount of padding required */
offset = MICRO_VARIANTS[variant + 102]; /* coefficient offset */
 
infoLine("Data Codewords: " + longueur);
infoLine("ECC Codewords: " + k);
 
/* We add the padding */
while (padding > 0) {
this.codeWords[this.codeWordCount] = 900;
this.codeWordCount++;
padding--;
}
 
/* Reed-Solomon error correction */
longueur = this.codeWordCount;
for (loop = 0; loop < 50; loop++) {
mccorrection[loop] = 0;
}
 
for (int i = 0; i < longueur; i++) {
total = (this.codeWords[i] + mccorrection[k - 1]) % 929;
for (j = k - 1; j >= 0; j--) {
if (j == 0) {
mccorrection[j] = (929 - total * MICRO_COEFFS[offset + j] % 929) % 929;
} else {
mccorrection[j] = (mccorrection[j - 1] + 929 - total * MICRO_COEFFS[offset + j] % 929) % 929;
}
}
}
 
for (j = 0; j < k; j++) {
if (mccorrection[j] != 0) {
mccorrection[j] = 929 - mccorrection[j];
}
}
/* we add these codes to the string */
for (int i = k - 1; i >= 0; i--) {
this.codeWords[this.codeWordCount] = mccorrection[i];
this.codeWordCount++;
}
 
/* Now get the RAP (Row Address Pattern) start values */
LeftRAPStart = RAP_TABLE[variant];
CentreRAPStart = RAP_TABLE[variant + 34];
RightRAPStart = RAP_TABLE[variant + 68];
StartCluster = RAP_TABLE[variant + 102] / 3;
 
/* That's all values loaded, get on with the encoding */
 
LeftRAP = LeftRAPStart;
CentreRAP = CentreRAPStart;
RightRAP = RightRAPStart;
Cluster = StartCluster; /*
* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and
* Cluster(6)
*/
 
this.readable = "";
this.pattern = new String[this.rows];
this.row_count = this.rows;
this.row_height = new int[this.rows];
 
infoLine("Grid Size: " + this.columns + " X " + this.row_count);
 
for (int i = 0; i < this.rows; i++) {
codebarre.setLength(0);
offset = 929 * Cluster;
for (j = 0; j < 5; j++) {
dummy[j] = 0;
}
for (j = 0; j < this.columns; j++) {
dummy[j + 1] = this.codeWords[i * this.columns + j];
}
 
/* Copy the data into codebarre */
codebarre.append(RAPLR[LeftRAP]);
codebarre.append('1');
codebarre.append(CODAGEMC[offset + dummy[1]]);
codebarre.append('1');
if (this.columns == 3) {
codebarre.append(RAPC[CentreRAP]);
}
if (this.columns >= 2) {
codebarre.append('1');
codebarre.append(CODAGEMC[offset + dummy[2]]);
codebarre.append('1');
}
if (this.columns == 4) {
codebarre.append(RAPC[CentreRAP]);
}
if (this.columns >= 3) {
codebarre.append('1');
codebarre.append(CODAGEMC[offset + dummy[3]]);
codebarre.append('1');
}
if (this.columns == 4) {
codebarre.append('1');
codebarre.append(CODAGEMC[offset + dummy[4]]);
codebarre.append('1');
}
codebarre.append(RAPLR[RightRAP]);
codebarre.append('1'); /* stop */
 
/* Now codebarre is a mixture of letters and numbers */
 
flip = 1;
bin.setLength(0);
for (loop = 0; loop < codebarre.length(); loop++) {
if (codebarre.charAt(loop) >= '0' && codebarre.charAt(loop) <= '9') {
for (k = 0; k < Character.getNumericValue(codebarre.charAt(loop)); k++) {
if (flip == 0) {
bin.append('0');
} else {
bin.append('1');
}
}
if (flip == 0) {
flip = 1;
} else {
flip = 0;
}
} else {
bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]);
}
}
 
/* so now pattern[] holds the string of '1's and '0's. - copy this to the symbol */
this.pattern[i] = bin2pat(bin);
this.row_height[i] = this.default_height;
 
/* Set up RAPs and Cluster for next row */
LeftRAP++;
CentreRAP++;
RightRAP++;
Cluster++;
 
if (LeftRAP == 53) {
LeftRAP = 1;
}
if (CentreRAP == 53) {
CentreRAP = 1;
}
if (RightRAP == 53) {
RightRAP = 1;
}
if (Cluster == 3) {
Cluster = 0;
}
}
}
 
private void validateRows(final int min, final int max) {
if (this.rows != null) {
if (this.rows < min) {
throw new OkapiException("Too few rows (" + this.rows + ")");
} else if (this.rows > max) {
throw new OkapiException("Too many rows (" + this.rows + ")");
}
}
}
 
private void validateColumns(final int min, final int max) {
if (this.columns != null) {
if (this.columns < min) {
throw new OkapiException("Too few columns (" + this.columns + ")");
} else if (this.columns > max) {
throw new OkapiException("Too many columns (" + this.columns + ")");
}
}
}
 
private static EncodingMode chooseMode(final int codeascii) {
if (codeascii >= '0' && codeascii <= '9') {
return EncodingMode.NUM;
} else if (codeascii == '\t' || codeascii == '\n' || codeascii == '\r' || codeascii >= ' ' && codeascii <= '~') {
return EncodingMode.TEX;
} else {
return EncodingMode.BYT;
}
}
 
private static int getMicroPdf417Variant(final int codeWordCount, final Integer columns, final Integer rows) {
for (int i = 0; i < 34; i++) {
final int maxCodewordCount = MICRO_AUTOSIZE[i];
if (codeWordCount <= maxCodewordCount) {
final int variant = MICRO_AUTOSIZE[i + 34];
final int columnsForThisVariant = MICRO_VARIANTS[variant - 1];
final int rowsForThisVariant = MICRO_VARIANTS[variant - 1 + 34];
if ((columns == null || columns == columnsForThisVariant) && (rows == null || rows == rowsForThisVariant)) {
return variant;
}
}
}
throw new OkapiException("Unable to determine MicroPDF417 variant for " + codeWordCount + " codewords");
}
 
/** Determines the encoding block groups for the specified data. */
private static List<Block> createBlocks(final int[] data) {
 
final List<Block> blocks = new ArrayList<>();
Block current = null;
 
for (int i = 0; i < data.length; i++) {
final EncodingMode mode = chooseMode(data[i]);
if (current != null && current.mode == mode && (mode != EncodingMode.NUM || current.length < MAX_NUMERIC_COMPACTION_BLOCK_SIZE)) {
current.length++;
} else {
current = new Block(mode);
blocks.add(current);
}
}
 
smoothBlocks(blocks);
 
return blocks;
}
 
/** Combines adjacent blocks of different types in very specific scenarios. */
private static void smoothBlocks(final List<Block> blocks) {
 
for (int i = 0; i < blocks.size(); i++) {
final Block block = blocks.get(i);
final EncodingMode last = i > 0 ? blocks.get(i - 1).mode : EncodingMode.FALSE;
final EncodingMode next = i < blocks.size() - 1 ? blocks.get(i + 1).mode : EncodingMode.FALSE;
if (block.mode == EncodingMode.NUM) {
if (i == 0) { /* first block */
if (next == EncodingMode.TEX && block.length < 8) {
block.mode = EncodingMode.TEX;
} else if (next == EncodingMode.BYT && block.length == 1) {
block.mode = EncodingMode.BYT;
}
} else if (i == blocks.size() - 1) { /* last block */
if (last == EncodingMode.TEX && block.length < 7) {
block.mode = EncodingMode.TEX;
} else if (last == EncodingMode.BYT && block.length == 1) {
block.mode = EncodingMode.BYT;
}
} else { /* not first or last block */
if (last == EncodingMode.BYT && next == EncodingMode.BYT && block.length < 4) {
block.mode = EncodingMode.BYT;
} else if (last == EncodingMode.BYT && next == EncodingMode.TEX && block.length < 4) {
block.mode = EncodingMode.TEX;
} else if (last == EncodingMode.TEX && next == EncodingMode.BYT && block.length < 5) {
block.mode = EncodingMode.TEX;
} else if (last == EncodingMode.TEX && next == EncodingMode.TEX && block.length < 8) {
block.mode = EncodingMode.TEX;
} else if (last == EncodingMode.NUM && next == EncodingMode.TEX && block.length < 8) {
block.mode = EncodingMode.TEX;
}
}
}
}
 
mergeBlocks(blocks);
 
for (int i = 0; i < blocks.size(); i++) {
final Block block = blocks.get(i);
final EncodingMode last = i > 0 ? blocks.get(i - 1).mode : EncodingMode.FALSE;
final EncodingMode next = i < blocks.size() - 1 ? blocks.get(i + 1).mode : EncodingMode.FALSE;
if (block.mode == EncodingMode.TEX && i > 0) { /* not the first */
if (i == blocks.size() - 1) { /* the last one */
if (last == EncodingMode.BYT && block.length == 1) {
block.mode = EncodingMode.BYT;
}
} else { /* not the last one */
if (last == EncodingMode.BYT && next == EncodingMode.BYT && block.length < 5) {
block.mode = EncodingMode.BYT;
}
if ((last == EncodingMode.BYT && next != EncodingMode.BYT || last != EncodingMode.BYT && next == EncodingMode.BYT) && block.length < 3) {
block.mode = EncodingMode.BYT;
}
}
}
}
 
mergeBlocks(blocks);
}
 
/** Combines adjacent blocks of the same type. */
private static void mergeBlocks(final List<Block> blocks) {
for (int i = 1; i < blocks.size(); i++) {
final Block b1 = blocks.get(i - 1);
final Block b2 = blocks.get(i);
if (b1.mode == b2.mode && (b1.mode != EncodingMode.NUM || b1.length + b2.length <= MAX_NUMERIC_COMPACTION_BLOCK_SIZE)) {
b1.length += b2.length;
blocks.remove(i);
i--;
}
}
}
 
private void processText(final int start, final int length, final boolean skipLatch) {
int j, blockIndext, curtable;
int codeascii;
int wnet = 0;
final int[] listet0 = new int[length];
final int[] listet1 = new int[length];
final int[] chainet = new int[length * 4];
 
/* listet will contain the table numbers and the value of each characters */
for (blockIndext = 0; blockIndext < length; blockIndext++) {
codeascii = this.inputData[start + blockIndext];
switch (codeascii) {
case '\t':
listet0[blockIndext] = 12;
listet1[blockIndext] = 12;
break;
case '\n':
listet0[blockIndext] = 8;
listet1[blockIndext] = 15;
break;
case 13:
listet0[blockIndext] = 12;
listet1[blockIndext] = 11;
break;
default:
listet0[blockIndext] = ASCII_X[codeascii - 32];
listet1[blockIndext] = ASCII_Y[codeascii - 32];
break;
}
}
 
curtable = 1; /* default table */
for (j = 0; j < length; j++) {
if ((listet0[j] & curtable) != 0) { /* The character is in the current table */
chainet[wnet] = listet1[j];
wnet++;
} else { /* Obliged to change table */
boolean flag = false; /* True if we change table for only one character */
if (j == length - 1) {
flag = true;
} else {
if ((listet0[j] & listet0[j + 1]) == 0) {
flag = true;
}
}
 
if (flag) { /* we change only one character - look for temporary switch */
if ((listet0[j] & 1) != 0 && curtable == 2) { /* T_UPP */
chainet[wnet] = 27;
chainet[wnet + 1] = listet1[j];
wnet += 2;
}
if ((listet0[j] & 8) != 0) { /* T_PUN */
chainet[wnet] = 29;
chainet[wnet + 1] = listet1[j];
wnet += 2;
}
if (!((listet0[j] & 1) != 0 && curtable == 2 || (listet0[j] & 8) != 0)) {
/* No temporary switch available */
flag = false;
}
}
 
if (!flag) {
int newtable;
 
if (j == length - 1) {
newtable = listet0[j];
} else {
if ((listet0[j] & listet0[j + 1]) == 0) {
newtable = listet0[j];
} else {
newtable = listet0[j] & listet0[j + 1];
}
}
 
/* Maintain the first if several tables are possible */
switch (newtable) {
case 3:
case 5:
case 7:
case 9:
case 11:
case 13:
case 15:
newtable = 1;
break;
case 6:
case 10:
case 14:
newtable = 2;
break;
case 12:
newtable = 4;
break;
}
 
/* select the switch */
switch (curtable) {
case 1:
switch (newtable) {
case 2:
chainet[wnet] = 27;
wnet++;
break;
case 4:
chainet[wnet] = 28;
wnet++;
break;
case 8:
chainet[wnet] = 28;
wnet++;
chainet[wnet] = 25;
wnet++;
break;
}
break;
case 2:
switch (newtable) {
case 1:
chainet[wnet] = 28;
wnet++;
chainet[wnet] = 28;
wnet++;
break;
case 4:
chainet[wnet] = 28;
wnet++;
break;
case 8:
chainet[wnet] = 28;
wnet++;
chainet[wnet] = 25;
wnet++;
break;
}
break;
case 4:
switch (newtable) {
case 1:
chainet[wnet] = 28;
wnet++;
break;
case 2:
chainet[wnet] = 27;
wnet++;
break;
case 8:
chainet[wnet] = 25;
wnet++;
break;
}
break;
case 8:
switch (newtable) {
case 1:
chainet[wnet] = 29;
wnet++;
break;
case 2:
chainet[wnet] = 29;
wnet++;
chainet[wnet] = 27;
wnet++;
break;
case 4:
chainet[wnet] = 29;
wnet++;
chainet[wnet] = 28;
wnet++;
break;
}
break;
}
curtable = newtable;
/* at last we add the character */
chainet[wnet] = listet1[j];
wnet++;
}
}
}
 
if ((wnet & 1) != 0) {
chainet[wnet] = 29;
wnet++;
}
 
/* Now translate the string chainet into codewords */
 
if (!skipLatch) {
// text compaction mode is the default mode for PDF417,
// so no need for an explicit latch if this is the first block
this.codeWords[this.codeWordCount] = 900;
this.codeWordCount++;
}
 
for (j = 0; j < wnet; j += 2) {
final int cw_number = 30 * chainet[j] + chainet[j + 1];
this.codeWords[this.codeWordCount] = cw_number;
this.codeWordCount++;
}
}
 
private void processBytes(int start, final int length, final EncodingMode lastMode) {
int len = 0;
int chunkLen = 0;
BigInteger mantisa;
BigInteger total;
BigInteger word;
 
mantisa = new BigInteger("0");
total = new BigInteger("0");
 
if (length == 1 && lastMode == EncodingMode.TEX) {
this.codeWords[this.codeWordCount++] = 913;
this.codeWords[this.codeWordCount++] = this.inputData[start];
} else {
/* select the switch for multiple of 6 bytes */
if (length % 6 == 0) {
this.codeWords[this.codeWordCount++] = 924;
} else {
this.codeWords[this.codeWordCount++] = 901;
}
 
while (len < length) {
chunkLen = length - len;
if (6 <= chunkLen) /* Take groups of 6 */ {
chunkLen = 6;
len += chunkLen;
total = BigInteger.valueOf(0);
 
while (chunkLen-- != 0) {
mantisa = BigInteger.valueOf(this.inputData[start++]);
total = total.or(mantisa.shiftLeft(chunkLen * 8));
}
 
chunkLen = 5;
 
while (chunkLen-- != 0) {
 
word = total.mod(BigInteger.valueOf(900));
this.codeWords[this.codeWordCount + chunkLen] = word.intValue();
total = total.divide(BigInteger.valueOf(900));
}
this.codeWordCount += 5;
} else /* If it remain a group of less than 6 bytes */ {
len += chunkLen;
while (chunkLen-- != 0) {
this.codeWords[this.codeWordCount++] = this.inputData[start++];
}
}
}
}
}
 
private void processNumbers(final int[] data, final int start, final int length, final boolean skipLatch) {
 
BigInteger tVal, dVal;
final int[] d = new int[16];
int cw_count;
 
if (!skipLatch) {
// we don't need to latch to numeric mode in some cases, e.g.
// during numeric compaction of the Macro PDF417 segment index
this.codeWords[this.codeWordCount++] = 902;
}
 
final StringBuilder t = new StringBuilder(length + 1);
t.append('1');
for (int i = 0; i < length; i++) {
t.append((char) data[start + i]);
}
 
tVal = new BigInteger(t.toString());
 
cw_count = 0;
do {
dVal = tVal.mod(BigInteger.valueOf(900));
d[cw_count] = dVal.intValue();
tVal = tVal.divide(BigInteger.valueOf(900));
cw_count++;
} while (tVal.compareTo(BigInteger.ZERO) == 1);
 
for (int i = cw_count - 1; i >= 0; i--) {
this.codeWords[this.codeWordCount++] = d[i];
}
}
 
/** Adds the Macro PDF417 control block codewords (if any). */
private void addMacroCodewords() {
 
// if the structured append series size is 1, this isn't
// actually part of a structured append series
if (this.structuredAppendTotal == 1) {
return;
}
 
// add the Macro marker codeword
this.codeWords[this.codeWordCount++] = 928;
 
// add the segment index, padded with leading zeros to five digits
// use numeric compaction, but no latch
int segmentIndex = this.structuredAppendPosition - 1;
final int[] data = new int[5];
for (int x = data.length - 1; x >= 0; x--) {
data[x] = '0' + segmentIndex % 10;
segmentIndex /= 10;
}
processNumbers(data, 0, data.length, true);
 
// add the file ID (base 900, which is easy since we limit
// file ID values to the range 0 to 899)
this.codeWords[this.codeWordCount++] = this.structuredAppendFileId;
 
// NOTE: we could add the optional segment count field here, but
// it doesn't appear to be necessary... if we do eventually decide
// to add it, it will probably be [923, 001, count1, count2]
 
// add the terminator to the last symbol of the series
final boolean last = this.structuredAppendPosition == this.structuredAppendTotal;
if (last) {
this.codeWords[this.codeWordCount++] = 922;
}
}
 
private static class Block {
 
public EncodingMode mode;
public int length;
 
public Block(final EncodingMode mode) {
this.mode = mode;
this.length = 1;
}
 
@Override
public String toString() {
return this.mode + "x" + this.length;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Symbol.java
New file
0,0 → 1,841
/*
* Copyright 2014-2018 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER;
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 static uk.org.okapibarcode.util.Arrays.containsAt;
import static uk.org.okapibarcode.util.Arrays.positionOf;
import static uk.org.okapibarcode.util.Doubles.roughlyEqual;
 
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
 
import uk.org.okapibarcode.output.Java2DRenderer;
import uk.org.okapibarcode.util.EciMode;
import uk.org.okapibarcode.util.Gs1;
 
/**
* Generic barcode symbology class.
*
* TODO: Setting attributes like module width, font size, etc should probably throw an exception if
* set *after* encoding has already been completed.
*
* TODO: GS1 data is encoded slightly differently depending on whether [AI]data content is used, or
* if FNC1 escape sequences are used. We may want to make sure that they encode to the same output.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public abstract class Symbol {
 
public static enum DataType {
ECI, GS1, HIBC
}
 
protected static final int FNC1 = -1;
protected static final int FNC2 = -2;
protected static final int FNC3 = -3;
protected static final int FNC4 = -4;
 
protected static final String FNC1_STRING = "\\<FNC1>";
protected static final String FNC2_STRING = "\\<FNC2>";
protected static final String FNC3_STRING = "\\<FNC3>";
protected static final String FNC4_STRING = "\\<FNC4>";
 
private static char[] HIBC_CHAR_TABLE = { '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', '-', '.', ' ', '$', '/', '+', '%' };
 
// user-specified values and settings
 
protected DataType inputDataType = DataType.ECI;
protected boolean readerInit;
protected int default_height = 40;
protected int quietZoneHorizontal = 0;
protected int quietZoneVertical = 0;
protected int moduleWidth = 1;
protected Font font;
protected String fontName = "Helvetica";
protected int fontSize = 8;
protected HumanReadableLocation humanReadableLocation = BOTTOM;
protected HumanReadableAlignment humanReadableAlignment = CENTER;
protected boolean emptyContentAllowed = false;
 
// internal state calculated when setContent() is called
 
protected String content;
protected int eciMode = -1;
protected int[] inputData; // usually bytes (values 0-255), but may also contain FNC flags
protected String readable = "";
protected String[] pattern;
protected int row_count = 0;
protected int[] row_height;
protected int symbol_height = 0;
protected int symbol_width = 0;
protected StringBuilder encodeInfo = new StringBuilder();
protected List<Rectangle2D.Double> rectangles = new ArrayList<>(); // note positions do not
// account for quiet zones
// (handled in renderers)
protected List<TextBox> texts = new ArrayList<>(); // note positions do not account for quiet
// zones (handled in renderers)
protected List<Hexagon> hexagons = new ArrayList<>(); // note positions do not account for quiet
// zones (handled in renderers)
protected List<Ellipse2D.Double> target = new ArrayList<>(); // note positions do not account
// for quiet zones (handled in
// renderers)
 
/**
* <p>
* Sets the type of input data. This setting influences what pre-processing is done on data
* before encoding in the symbol. For example: for <code>GS1</code> mode the AI data will be
* used to calculate the position of 'FNC1' characters.
*
* <p>
* Valid values are:
*
* <ul>
* <li><code>ECI</code> Extended Channel Interpretations (default)
* <li><code>GS1</code> Application Identifier and data pairs in "[AI]DATA" format
* <li><code>HIBC</code> Health Industry Bar Code number (without check digit)
* </ul>
*
* @param dataType the type of input data
*/
public void setDataType(final DataType dataType) {
if (dataType == DataType.GS1 && !gs1Supported()) {
throw new IllegalArgumentException("This symbology type does not support GS1 data.");
}
this.inputDataType = dataType;
}
 
/**
* Returns the type of input data in this symbol.
*
* @return the type of input data in this symbol
*/
public DataType getDataType() {
return this.inputDataType;
}
 
/**
* Returns <code>true</code> if this type of symbology supports GS1 data.
*
* @return <code>true</code> if this type of symbology supports GS1 data
*/
protected boolean gs1Supported() {
return false;
}
 
/**
* If set to <code>true</code>, the symbol is prefixed with a "Reader Initialization" or "Reader
* Programming" instruction.
*
* @param readerInit whether or not to enable reader initialization
*/
public void setReaderInit(final boolean readerInit) {
this.readerInit = readerInit;
}
 
/**
* Returns whether or not reader initialization is enabled.
*
* @return whether or not reader initialization is enabled
*/
public boolean getReaderInit() {
return this.readerInit;
}
 
/**
* Sets the default bar height for this symbol (default value is <code>40</code>).
*
* @param barHeight the default bar height for this symbol
*/
public void setBarHeight(final int barHeight) {
this.default_height = barHeight;
}
 
/**
* Returns the default bar height for this symbol.
*
* @return the default bar height for this symbol
*/
public int getBarHeight() {
return this.default_height;
}
 
/**
* Sets the module width for this symbol (default value is <code>1</code>).
*
* @param moduleWidth the module width for this symbol
*/
public void setModuleWidth(final int moduleWidth) {
this.moduleWidth = moduleWidth;
}
 
/**
* Returns the module width for this symbol.
*
* @return the module width for this symbol
*/
public int getModuleWidth() {
return this.moduleWidth;
}
 
/**
* Sets the horizontal quiet zone (white space) added to the left and to the right of this
* symbol.
*
* @param quietZoneHorizontal the horizontal quiet zone (white space) added to the left and to
* the right of this symbol
*/
public void setQuietZoneHorizontal(final int quietZoneHorizontal) {
this.quietZoneHorizontal = quietZoneHorizontal;
}
 
/**
* Returns the horizontal quiet zone (white space) added to the left and to the right of this
* symbol.
*
* @return the horizontal quiet zone (white space) added to the left and to the right of this
* symbol
*/
public int getQuietZoneHorizontal() {
return this.quietZoneHorizontal;
}
 
/**
* Sets the vertical quiet zone (white space) added above and below this symbol.
*
* @param quietZoneVertical the vertical quiet zone (white space) added above and below this
* symbol
*/
public void setQuietZoneVertical(final int quietZoneVertical) {
this.quietZoneVertical = quietZoneVertical;
}
 
/**
* Returns the vertical quiet zone (white space) added above and below this symbol.
*
* @return the vertical quiet zone (white space) added above and below this symbol
*/
public int getQuietZoneVertical() {
return this.quietZoneVertical;
}
 
/**
* <p>
* Sets the font to use to render the human-readable text. This is an alternative to setting the
* {@link #setFontName(String) font name} and {@link #setFontSize(int) font size} separately.
* May allow some applications to avoid the use of
* {@link GraphicsEnvironment#registerFont(Font)} when using the {@link Java2DRenderer}.
*
* <p>
* Do not use this method in combination with {@link #setFontName(String)} or
* {@link #setFontSize(int)}.
*
* @param font the font to use to render the human-readable text
*/
public void setFont(final Font font) {
this.font = font;
this.fontName = font.getFontName();
this.fontSize = font.getSize();
}
 
/**
* Returns the font to use to render the human-readable text.
*
* @return the font to use to render the human-readable text
*/
public Font getFont() {
return this.font;
}
 
/**
* <p>
* Sets the name of the font to use to render the human-readable text (default value is
* <code>Helvetica</code>). The specified font name needs to be registered via
* {@link GraphicsEnvironment#registerFont(Font)} if you are using the {@link Java2DRenderer}.
* In order to set the font without registering the font with the graphics environment when
* using the {@link Java2DRenderer}, you may need to use {@link #setFont(Font)} instead.
*
* <p>
* Use this method in combination with {@link #setFontSize(int)}.
*
* <p>
* Do not use this method in combination with {@link #setFont(Font)}.
*
* @param fontName the name of the font to use to render the human-readable text
*/
public void setFontName(final String fontName) {
this.fontName = Objects.requireNonNull(fontName, "font name may not be null");
this.font = null;
}
 
/**
* Returns the name of the font to use to render the human-readable text.
*
* @return the name of the font to use to render the human-readable text
*/
public String getFontName() {
return this.fontName;
}
 
/**
* <p>
* Sets the size of the font to use to render the human-readable text (default value is
* <code>8</code>).
*
* <p>
* Use this method in combination with {@link #setFontName(String)}.
*
* <p>
* Do not use this method in combination with {@link #setFont(Font)}.
*
* @param fontSize the size of the font to use to render the human-readable text
*/
public void setFontSize(final int fontSize) {
this.fontSize = fontSize;
this.font = null;
}
 
/**
* Returns the size of the font to use to render the human-readable text.
*
* @return the size of the font to use to render the human-readable text
*/
public int getFontSize() {
return this.fontSize;
}
 
/**
* Gets the width of the encoded symbol, including the horizontal quiet zone.
*
* @return the width of the encoded symbol
*/
public int getWidth() {
return this.symbol_width + 2 * this.quietZoneHorizontal;
}
 
/**
* Returns the height of the symbol, including the human-readable text, if any, as well as the
* vertical quiet zone. This height is an approximation, since it is calculated without access
* to a font engine.
*
* @return the height of the symbol, including the human-readable text, if any, as well as the
* vertical quiet zone
*/
public int getHeight() {
return this.symbol_height + getHumanReadableHeight() + 2 * this.quietZoneVertical;
}
 
/**
* Returns the height of the human-readable text, including the space between the text and other
* symbols. This height is an approximation, since it is calculated without access to a font
* engine.
*
* @return the height of the human-readable text
*/
public int getHumanReadableHeight() {
if (this.texts.isEmpty()) {
return 0;
} else {
return getTheoreticalHumanReadableHeight();
}
}
 
/**
* Returns the height of the human-readable text, assuming this symbol had human-readable text.
*
* @return the height of the human-readable text, assuming this symbol had human-readable text
*/
protected int getTheoreticalHumanReadableHeight() {
return (int) Math.ceil(this.fontSize * 1.2); // 0.2 space between bars and text
}
 
/**
* Returns a human readable summary of the decisions made by the encoder when creating a symbol.
*
* @return a human readable summary of the decisions made by the encoder when creating a symbol
*/
public String getEncodeInfo() {
return this.encodeInfo.toString();
}
 
/**
* Returns the ECI mode used by this symbol. The ECI mode is chosen automatically during
* encoding if the symbol data type has been set to {@link DataType#ECI}. If this symbol does
* not use ECI, this method will return <code>-1</code>.
*
* @return the ECI mode used by this symbol
* @see #eciProcess()
*/
public int getEciMode() {
return this.eciMode;
}
 
/**
* Sets the location of the human-readable text (default value is
* {@link HumanReadableLocation#BOTTOM}).
*
* @param humanReadableLocation the location of the human-readable text
*/
public void setHumanReadableLocation(final HumanReadableLocation humanReadableLocation) {
this.humanReadableLocation = humanReadableLocation;
}
 
/**
* Returns the location of the human-readable text.
*
* @return the location of the human-readable text
*/
public HumanReadableLocation getHumanReadableLocation() {
return this.humanReadableLocation;
}
 
/**
* Sets the text alignment of the human-readable text (default value is
* {@link HumanReadableAlignment#CENTER}).
*
* @param humanReadableAlignment the text alignment of the human-readable text
*/
public void setHumanReadableAlignment(final HumanReadableAlignment humanReadableAlignment) {
this.humanReadableAlignment = humanReadableAlignment;
}
 
/**
* Returns the text alignment of the human-readable text.
*
* @return the text alignment of the human-readable text
*/
public HumanReadableAlignment getHumanReadableAlignment() {
return this.humanReadableAlignment;
}
 
/**
* Returns render information about the rectangles in this symbol.
*
* @return render information about the rectangles in this symbol
*/
public List<Rectangle2D.Double> getRectangles() {
return this.rectangles;
}
 
/**
* Returns render information about the text elements in this symbol.
*
* @return render information about the text elements in this symbol
*/
public List<TextBox> getTexts() {
return this.texts;
}
 
/**
* Returns render information about the hexagons in this symbol.
*
* @return render information about the hexagons in this symbol
*/
public List<Hexagon> getHexagons() {
return this.hexagons;
}
 
/**
* Returns render information about the target circles in this symbol.
*
* @return render information about the target circles in this symbol
*/
public List<Ellipse2D.Double> getTarget() {
return this.target;
}
 
protected static String bin2pat(final CharSequence bin) {
 
int len = 0;
boolean black = true;
final StringBuilder pattern = new StringBuilder(bin.length());
 
for (int i = 0; i < bin.length(); i++) {
if (black) {
if (bin.charAt(i) == '1') {
len++;
} else {
black = false;
pattern.append((char) (len + '0'));
len = 1;
}
} else {
if (bin.charAt(i) == '0') {
len++;
} else {
black = true;
pattern.append((char) (len + '0'));
len = 1;
}
}
}
 
pattern.append((char) (len + '0'));
return pattern.toString();
}
 
/**
* Sets whether or not empty content is allowed. Some symbologies may be able to generate empty
* symbols when no data is present, though this is not usually desired behavior. The default
* value is <code>false</code> (no empty content allowed).
*
* @param emptyContentAllowed whether or not empty content is allowed
*/
public void setEmptyContentAllowed(final boolean emptyContentAllowed) {
this.emptyContentAllowed = emptyContentAllowed;
}
 
/**
* Returns whether or not empty content is allowed.
*
* @return whether or not empty content is allowed
*/
public boolean getEmptyContentAllowed() {
return this.emptyContentAllowed;
}
 
/**
* Sets the data to be encoded and triggers encoding. Input data will be assumed to be of the
* type set by {@link #setDataType(DataType)}.
*
* @param data the data to encode
* @throws OkapiException if no data or data is invalid
*/
public void setContent(String data) {
 
if (data == null) {
data = "";
}
 
this.encodeInfo.setLength(0); // clear
 
switch (this.inputDataType) {
case GS1:
this.content = Gs1.verify(data, FNC1_STRING);
this.readable = data.replace('[', '(').replace(']', ')');
break;
case HIBC:
this.content = hibcProcess(data);
break;
default:
this.content = data;
break;
}
 
if (this.content.isEmpty() && !this.emptyContentAllowed) {
throw new OkapiException("No input data");
}
 
encode();
plotSymbol();
mergeVerticalBlocks();
}
 
/**
* Returns the content encoded by this symbol.
*
* @return the content encoded by this symbol
*/
public String getContent() {
return this.content;
}
 
/**
* Returns the human-readable text for this symbol.
*
* @return the human-readable text for this symbol
*/
public String getHumanReadableText() {
return this.readable;
}
 
/**
* Chooses the ECI mode most suitable for the content of this symbol.
*/
protected void eciProcess() {
 
final EciMode eci = EciMode.of(this.content, "ISO8859_1", 3).or(this.content, "ISO8859_2", 4).or(this.content, "ISO8859_3", 5).or(this.content, "ISO8859_4", 6).or(this.content, "ISO8859_5", 7)
.or(this.content, "ISO8859_6", 8).or(this.content, "ISO8859_7", 9).or(this.content, "ISO8859_8", 10).or(this.content, "ISO8859_9", 11).or(this.content, "ISO8859_10", 12)
.or(this.content, "ISO8859_11", 13).or(this.content, "ISO8859_13", 15).or(this.content, "ISO8859_14", 16).or(this.content, "ISO8859_15", 17).or(this.content, "ISO8859_16", 18)
.or(this.content, "Windows_1250", 21).or(this.content, "Windows_1251", 22).or(this.content, "Windows_1252", 23).or(this.content, "Windows_1256", 24).or(this.content, "SJIS", 20)
.or(this.content, "UTF8", 26);
 
if (EciMode.NONE.equals(eci)) {
throw new OkapiException("Unable to determine ECI mode.");
}
 
this.eciMode = eci.mode;
this.inputData = toBytes(this.content, eci.charset);
 
infoLine("ECI Mode: " + eci.mode);
infoLine("ECI Charset: " + eci.charset.name());
}
 
protected static int[] toBytes(final String s, final Charset charset, final int... suffix) {
 
if (!charset.newEncoder().canEncode(s)) {
return null;
}
 
final byte[] fnc1 = FNC1_STRING.getBytes(charset);
final byte[] fnc2 = FNC2_STRING.getBytes(charset);
final byte[] fnc3 = FNC3_STRING.getBytes(charset);
final byte[] fnc4 = FNC4_STRING.getBytes(charset);
 
final byte[] bytes = s.getBytes(charset);
int[] data = new int[bytes.length + suffix.length];
 
int i = 0, j = 0;
for (; i < bytes.length; i++, j++) {
if (containsAt(bytes, fnc1, i)) {
data[j] = FNC1;
i += fnc1.length - 1;
} else if (containsAt(bytes, fnc2, i)) {
data[j] = FNC2;
i += fnc1.length - 1;
} else if (containsAt(bytes, fnc3, i)) {
data[j] = FNC3;
i += fnc1.length - 1;
} else if (containsAt(bytes, fnc4, i)) {
data[j] = FNC4;
i += fnc1.length - 1;
} else {
data[j] = bytes[i] & 0xff;
}
}
 
int k = 0;
for (; k < suffix.length; k++) {
data[j + k] = suffix[k];
}
 
if (j + k < i) {
data = Arrays.copyOf(data, j + k);
}
 
return data;
}
 
protected abstract void encode();
 
protected void plotSymbol() {
int xBlock, yBlock;
double x, y, w, h;
boolean black;
 
this.rectangles.clear();
this.texts.clear();
 
int baseY;
if (this.humanReadableLocation == TOP) {
baseY = getTheoreticalHumanReadableHeight();
} else {
baseY = 0;
}
 
h = 0;
y = baseY;
 
for (yBlock = 0; yBlock < this.row_count; yBlock++) {
black = true;
x = 0;
for (xBlock = 0; xBlock < this.pattern[yBlock].length(); xBlock++) {
final char c = this.pattern[yBlock].charAt(xBlock);
w = getModuleWidth(c - '0') * this.moduleWidth;
if (black) {
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 = (int) Math.ceil(x + w);
}
}
black = !black;
x += w;
}
if (y - baseY + h > this.symbol_height) {
this.symbol_height = (int) Math.ceil(y - baseY + h);
}
y += h;
}
 
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));
}
}
 
/**
* Returns the module width to use for the specified original module width, taking into account
* any module width ratio customizations. Intended to be overridden by subclasses that support
* such module width ratio customization.
*
* @param originalWidth the original module width
* @return the module width to use for the specified original module width
*/
protected double getModuleWidth(final int originalWidth) {
return originalWidth;
}
 
/**
* Search for rectangles which have the same width and x position, and which join together
* vertically and merge them together to reduce the number of rectangles needed to describe a
* symbol. This can actually take a non-trivial amount of time for symbols with a large number
* of rectangles (like large PDF417 symbols) so we exploit the fact that the rectangles are
* ordered by rows (and within the rows that they are ordered by x position).
*/
protected void mergeVerticalBlocks() {
 
final int before = this.rectangles.size();
 
for (int i = this.rectangles.size() - 1; i >= 0; i--) {
final Rectangle2D.Double rect1 = this.rectangles.get(i);
for (int j = i - 1; j >= 0; j--) {
final Rectangle2D.Double rect2 = this.rectangles.get(j);
if (roughlyEqual(rect1.y, rect2.y + rect2.height)) {
// rect2 is in the segment of rectangles for the row directly above rect1
if (roughlyEqual(rect1.x, rect2.x) && roughlyEqual(rect1.width, rect2.width)) {
// we've found a match; merge the rectangles
rect2.height += rect1.height;
this.rectangles.remove(i);
break;
}
if (rect2.x < rect1.x) {
// we've moved past any rectangles that might be directly above rect1
break;
}
}
}
}
 
final int after = this.rectangles.size();
if (before != after) {
infoLine("Blocks Merged: " + before + " -> " + after);
}
}
 
/**
* Adds the HIBC prefix and check digit to the specified data, returning the resultant data
* string.
*
* @see <a href=
* "https://sourceforge.net/p/zint/code/ci/master/tree/backend/library.c">Corresponding
* Zint code</a>
*/
private String hibcProcess(String source) {
 
// HIBC 2.6 allows up to 110 characters, not including the "+" prefix or the check digit
if (source.length() > 110) {
throw new OkapiException("Data too long for HIBC LIC");
}
 
source = source.toUpperCase();
if (!source.matches("[A-Z0-9-\\. \\$/+\\%]+?")) {
throw new OkapiException("Invalid characters in input");
}
 
int counter = 41;
for (int i = 0; i < source.length(); i++) {
counter += positionOf(source.charAt(i), HIBC_CHAR_TABLE);
}
counter = counter % 43;
 
final char checkDigit = HIBC_CHAR_TABLE[counter];
 
infoLine("HIBC Check Digit Counter: " + counter);
infoLine("HIBC Check Digit: " + checkDigit);
 
return "+" + source + checkDigit;
}
 
/**
* Returns the intermediate coding of this bar code. Symbol types that use the test
* infrastructure should override this method.
*
* @return the intermediate coding of this bar code
*/
protected int[] getCodewords() {
throw new UnsupportedOperationException();
}
 
/**
* Returns this bar code's pattern, converted into a set of corresponding codewords. Useful for
* bar codes that encode their content as a pattern.
*
* @param size the number of digits in each codeword
* @return this bar code's pattern, converted into a set of corresponding codewords
*/
protected int[] getPatternAsCodewords(final int size) {
if (size >= 10) {
throw new IllegalArgumentException("Pattern groups of 10 or more digits are likely to be too large to parse as integers.");
}
if (this.pattern == null || this.pattern.length == 0) {
return new int[0];
} else {
final int count = (int) Math.ceil(this.pattern[0].length() / (double) size);
final int[] codewords = new int[this.pattern.length * count];
for (int i = 0; i < this.pattern.length; i++) {
final String row = this.pattern[i];
for (int j = 0; j < count; j++) {
final int substringStart = j * size;
final int substringEnd = Math.min((j + 1) * size, row.length());
codewords[i * count + j] = Integer.parseInt(row.substring(substringStart, substringEnd));
}
}
return codewords;
}
}
 
protected void info(final CharSequence s) {
this.encodeInfo.append(s);
}
 
protected void infoSpace(final int i) {
this.encodeInfo.append(i).append(' ');
}
 
protected void infoSpace(final char c) {
this.encodeInfo.append(c).append(' ');
}
 
protected void infoLine(final CharSequence s) {
this.encodeInfo.append(s).append('\n');
}
 
protected void infoLine() {
this.encodeInfo.append('\n');
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/DataBarLimited.java
New file
0,0 → 1,423
/*
* Copyright 2014-2018 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import java.math.BigInteger;
 
/**
* <p>
* Implements GS1 DataBar Limited according to ISO/IEC 24724:2011.
*
* <p>
* Input data should be a 12-digit or 13-digit Global Trade Identification Number (GTIN) without
* check digit or Application Identifier [01].
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class DataBarLimited extends Symbol {
 
private static final int[] T_EVEN_LTD = { 28, 728, 6454, 203, 2408, 1, 16632 };
 
private static final int[] MODULES_ODD_LTD = { 17, 13, 9, 15, 11, 19, 7 };
 
private static final int[] MODULES_EVEN_LTD = { 9, 13, 17, 11, 15, 7, 19 };
 
private static final int[] WIDEST_ODD_LTD = { 6, 5, 3, 5, 4, 8, 1 };
 
private static final int[] WIDEST_EVEN_LTD = { 3, 4, 6, 4, 5, 1, 8 };
 
private static final int[] CHECKSUM_WEIGHT_LTD = { /* Table 7 */
1, 3, 9, 27, 81, 65, 17, 51, 64, 14, 42, 37, 22, 66, 20, 60, 2, 6, 18, 54, 73, 41, 34, 13, 39, 28, 84, 74 };
 
private static final int[] FINDER_PATTERN_LTD = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 3, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1,
2, 1, 1, 3, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1,
1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 3, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 2, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1,
1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1,
1, 1, 1, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1,
1, 2, 1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 2, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 2,
1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 1, 1, 1,
1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 1, 2,
1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 2, 1, 1, 1,
1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1,
1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1,
2, 1, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1,
2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1,
3, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1,
1 /* ISO/IEC 24724-2011 57 */
};
 
private boolean linkageFlag;
 
/**
* Although this is a GS1 symbology, input data is expected to omit the [01] Application
* Identifier, as well as the check digit. Thus, the input data is not considered GS1-format
* data.
*/
@Override
protected boolean gs1Supported() {
return false;
}
 
protected void setLinkageFlag() {
this.linkageFlag = true;
}
 
protected void unsetLinkageFlag() {
this.linkageFlag = false;
}
 
@Override
protected void encode() {
BigInteger accum;
BigInteger left_reg;
BigInteger right_reg;
int left_group;
int right_group;
int i, j;
int left_character;
int right_character;
int left_odd;
int right_odd;
int left_even;
int right_even;
final int[] left_widths = new int[14];
final int[] right_widths = new int[14];
int checksum;
final int[] check_elements = new int[14];
final int[] total_widths = new int[46];
boolean bar_latch;
int check_digit = 0;
int count = 0;
String hrt;
int compositeOffset = 0;
 
if (this.content.length() > 13) {
throw new OkapiException("Input too long");
}
 
if (!this.content.matches("[0-9]+?")) {
throw new OkapiException("Invalid characters in input");
}
 
if (this.content.length() == 13 && this.content.charAt(0) != '0' && this.content.charAt(0) != '1') {
throw new OkapiException("Input out of range");
}
 
accum = new BigInteger(this.content);
 
if (this.linkageFlag) {
/* Add symbol linkage flag */
accum = accum.add(new BigInteger("2015133531096"));
}
 
/* Calculate left and right pair values */
left_reg = accum.divide(new BigInteger("2013571"));
right_reg = accum.mod(new BigInteger("2013571"));
 
left_group = 0;
if (left_reg.compareTo(new BigInteger("183063")) == 1) {
left_group = 1;
}
if (left_reg.compareTo(new BigInteger("820063")) == 1) {
left_group = 2;
}
if (left_reg.compareTo(new BigInteger("1000775")) == 1) {
left_group = 3;
}
if (left_reg.compareTo(new BigInteger("1491020")) == 1) {
left_group = 4;
}
if (left_reg.compareTo(new BigInteger("1979844")) == 1) {
left_group = 5;
}
if (left_reg.compareTo(new BigInteger("1996938")) == 1) {
left_group = 6;
}
 
right_group = 0;
if (right_reg.compareTo(new BigInteger("183063")) == 1) {
right_group = 1;
}
if (right_reg.compareTo(new BigInteger("820063")) == 1) {
right_group = 2;
}
if (right_reg.compareTo(new BigInteger("1000775")) == 1) {
right_group = 3;
}
if (right_reg.compareTo(new BigInteger("1491020")) == 1) {
right_group = 4;
}
if (right_reg.compareTo(new BigInteger("1979844")) == 1) {
right_group = 5;
}
if (right_reg.compareTo(new BigInteger("1996938")) == 1) {
right_group = 6;
}
 
infoLine("Data Characters: " + (left_group + 1) + " " + (right_group + 1));
 
switch (left_group) {
case 1:
left_reg = left_reg.subtract(new BigInteger("183064"));
break;
case 2:
left_reg = left_reg.subtract(new BigInteger("820064"));
break;
case 3:
left_reg = left_reg.subtract(new BigInteger("1000776"));
break;
case 4:
left_reg = left_reg.subtract(new BigInteger("1491021"));
break;
case 5:
left_reg = left_reg.subtract(new BigInteger("1979845"));
break;
case 6:
left_reg = left_reg.subtract(new BigInteger("1996939"));
break;
}
 
switch (right_group) {
case 1:
right_reg = right_reg.subtract(new BigInteger("183064"));
break;
case 2:
right_reg = right_reg.subtract(new BigInteger("820064"));
break;
case 3:
right_reg = right_reg.subtract(new BigInteger("1000776"));
break;
case 4:
right_reg = right_reg.subtract(new BigInteger("1491021"));
break;
case 5:
right_reg = right_reg.subtract(new BigInteger("1979845"));
break;
case 6:
right_reg = right_reg.subtract(new BigInteger("1996939"));
break;
}
 
left_character = left_reg.intValue();
right_character = right_reg.intValue();
 
left_odd = left_character / T_EVEN_LTD[left_group];
left_even = left_character % T_EVEN_LTD[left_group];
right_odd = right_character / T_EVEN_LTD[right_group];
right_even = right_character % T_EVEN_LTD[right_group];
 
int[] widths = getWidths(left_odd, MODULES_ODD_LTD[left_group], 7, WIDEST_ODD_LTD[left_group], 1);
left_widths[0] = widths[0];
left_widths[2] = widths[1];
left_widths[4] = widths[2];
left_widths[6] = widths[3];
left_widths[8] = widths[4];
left_widths[10] = widths[5];
left_widths[12] = widths[6];
 
widths = getWidths(left_even, MODULES_EVEN_LTD[left_group], 7, WIDEST_EVEN_LTD[left_group], 0);
left_widths[1] = widths[0];
left_widths[3] = widths[1];
left_widths[5] = widths[2];
left_widths[7] = widths[3];
left_widths[9] = widths[4];
left_widths[11] = widths[5];
left_widths[13] = widths[6];
 
widths = getWidths(right_odd, MODULES_ODD_LTD[right_group], 7, WIDEST_ODD_LTD[right_group], 1);
right_widths[0] = widths[0];
right_widths[2] = widths[1];
right_widths[4] = widths[2];
right_widths[6] = widths[3];
right_widths[8] = widths[4];
right_widths[10] = widths[5];
right_widths[12] = widths[6];
 
widths = getWidths(right_even, MODULES_EVEN_LTD[right_group], 7, WIDEST_EVEN_LTD[right_group], 0);
right_widths[1] = widths[0];
right_widths[3] = widths[1];
right_widths[5] = widths[2];
right_widths[7] = widths[3];
right_widths[9] = widths[4];
right_widths[11] = widths[5];
right_widths[13] = widths[6];
 
checksum = 0;
/* Calculate the checksum */
for (i = 0; i < 14; i++) {
checksum += CHECKSUM_WEIGHT_LTD[i] * left_widths[i];
checksum += CHECKSUM_WEIGHT_LTD[i + 14] * right_widths[i];
}
checksum %= 89;
 
infoLine("Checksum: " + checksum);
 
for (i = 0; i < 14; i++) {
check_elements[i] = FINDER_PATTERN_LTD[i + checksum * 14];
}
 
total_widths[0] = 1;
total_widths[1] = 1;
total_widths[44] = 1;
total_widths[45] = 1;
for (i = 0; i < 14; i++) {
total_widths[i + 2] = left_widths[i];
total_widths[i + 16] = check_elements[i];
total_widths[i + 30] = right_widths[i];
}
 
final StringBuilder bin = new StringBuilder();
final StringBuilder notbin = new StringBuilder();
 
bar_latch = false;
for (i = 0; i < 46; i++) {
for (j = 0; j < total_widths[i]; j++) {
if (bar_latch) {
bin.append('1');
notbin.append('0');
} else {
bin.append('0');
notbin.append('1');
}
}
if (bar_latch) {
bar_latch = false;
} else {
bar_latch = true;
}
}
 
/* Calculate check digit from Annex A and place human readable text */
 
this.readable = "(01)";
hrt = "";
for (i = this.content.length(); i < 13; i++) {
hrt += "0";
}
hrt += this.content;
 
for (i = 0; i < 13; i++) {
count += hrt.charAt(i) - '0';
if ((i & 1) == 0) {
count += 2 * (hrt.charAt(i) - '0');
}
}
 
check_digit = 10 - count % 10;
if (check_digit == 10) {
check_digit = 0;
}
 
hrt += (char) (check_digit + '0');
this.readable += hrt;
 
if (this.linkageFlag) {
compositeOffset = 1;
}
 
this.row_count = 1 + compositeOffset;
this.row_height = new int[1 + compositeOffset];
this.row_height[0 + compositeOffset] = -1;
this.pattern = new String[1 + compositeOffset];
this.pattern[0 + compositeOffset] = bin2pat(bin);
 
if (this.linkageFlag) {
// Add composite symbol separator
notbin.delete(70, notbin.length());
notbin.delete(0, 4);
this.row_height[0] = 1;
this.pattern[0] = "04" + bin2pat(notbin);
}
}
 
private static int getCombinations(final int n, final int r) {
 
int i, j;
int maxDenom, minDenom;
int val;
 
if (n - r > r) {
minDenom = r;
maxDenom = n - r;
} else {
minDenom = n - r;
maxDenom = r;
}
 
val = 1;
j = 1;
 
for (i = n; i > maxDenom; i--) {
val *= i;
if (j <= minDenom) {
val /= j;
j++;
}
}
 
for (; j <= minDenom; j++) {
val /= j;
}
 
return val;
}
 
static int[] getWidths(int val, int n, final int elements, final int maxWidth, final int noNarrow) {
 
int bar;
int elmWidth;
int mxwElement;
int subVal, lessVal;
int narrowMask = 0;
final int[] widths = new int[elements];
 
for (bar = 0; bar < elements - 1; bar++) {
for (elmWidth = 1, narrowMask |= 1 << bar;; elmWidth++, narrowMask &= ~(1 << bar)) {
/* get all combinations */
subVal = getCombinations(n - elmWidth - 1, elements - bar - 2);
/* less combinations with no single-module element */
if (noNarrow == 0 && narrowMask == 0 && n - elmWidth - (elements - bar - 1) >= elements - bar - 1) {
subVal -= getCombinations(n - elmWidth - (elements - bar), elements - bar - 2);
}
/* less combinations with elements > maxVal */
if (elements - bar - 1 > 1) {
lessVal = 0;
for (mxwElement = n - elmWidth - (elements - bar - 2); mxwElement > maxWidth; mxwElement--) {
lessVal += getCombinations(n - elmWidth - mxwElement - 1, elements - bar - 3);
}
subVal -= lessVal * (elements - 1 - bar);
} else if (n - elmWidth > maxWidth) {
subVal--;
}
val -= subVal;
if (val < 0) {
break;
}
}
val += subVal;
n -= elmWidth;
widths[bar] = elmWidth;
}
 
widths[bar] = n;
 
return widths;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code2Of5.java
New file
0,0 → 1,469
/*
* 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;
 
/**
* Implements the Code 2 of 5 family of barcode standards.
*
* @author <a href="mailto:jakel2006@me.com">Robert Elliott</a>
*/
public class Code2Of5 extends Symbol {
 
public enum ToFMode {
/**
* Standard Code 2 of 5 mode, also known as Code 2 of 5 Matrix. Encodes any length numeric
* input (digits 0-9). This is the default mode.
*/
MATRIX,
/**
* Industrial Code 2 of 5 which can encode any length numeric input (digits 0-9) and does
* not include a check digit.
*/
INDUSTRIAL,
/**
* International Air Transport Agency variation of Code 2 of 5. Encodes any length numeric
* input (digits 0-9) and does not include a check digit.
*/
IATA,
/**
* Code 2 of 5 Data Logic. Encodes any length numeric input (digits 0-9) and does not
* include a check digit.
*/
DATA_LOGIC,
/**
* Interleaved Code 2 of 5. Encodes pairs of numbers, and so can only encode an even number
* of digits (0-9). If an odd number of digits is entered a leading zero is added. No check
* digit is calculated.
*/
INTERLEAVED,
/**
* Interleaved Code 2 of 5 with check digit. Encodes pairs of numbers, and so can only
* encode an even number of digits (0-9). If adding the check digit results in an odd number
* of digits then a leading zero is added.
*/
INTERLEAVED_WITH_CHECK_DIGIT,
/**
* ITF-14, also known as UPC Shipping Container Symbol or Case Code. Requires a 13-digit
* numeric input (digits 0-9). One modulo-10 check digit is calculated.
*/
ITF14,
/**
* Deutsche Post Leitcode. Requires a 13-digit numerical input. Check digit is calculated.
*/
DP_LEITCODE,
/**
* Deutsche Post Identcode. Requires an 11-digit numerical input. Check digit is calculated.
*/
DP_IDENTCODE
}
 
private static final String[] C25_MATRIX_TABLE = { "113311", "311131", "131131", "331111", "113131", "313111", "133111", "111331", "311311", "131311" };
 
private static final String[] C25_INDUSTRIAL_TABLE = { "1111313111", "3111111131", "1131111131", "3131111111", "1111311131", "3111311111", "1131311111", "1111113131", "3111113111", "1131113111" };
 
private static final String[] C25_INTERLEAVED_TABLE = { "11331", "31113", "13113", "33111", "11313", "31311", "13311", "11133", "31131", "13131" };
 
/** The 2-of-5 mode. */
private ToFMode mode = ToFMode.MATRIX;
 
/** Ratio of wide bar width to narrow bar width. */
private double moduleWidthRatio = 3;
 
/**
* Sets the 2-of-5 mode. The default value is {@link ToFMode#MATRIX}.
*
* @param mode the 2-of-5 mode
*/
public void setMode(final ToFMode mode) {
this.mode = mode;
}
 
/**
* Returns the 2-of-5 mode.
*
* @return the 2-of-5 mode
*/
public ToFMode getMode() {
return this.mode;
}
 
/**
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually between
* {@code 2} and {@code 3}. The default value is {@code 3}.
*
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width
*/
public void setModuleWidthRatio(final double moduleWidthRatio) {
this.moduleWidthRatio = moduleWidthRatio;
}
 
/**
* Returns the ratio of wide bar width to narrow bar width.
*
* @return the ratio of wide bar width to narrow bar width
*/
public double getModuleWidthRatio() {
return this.moduleWidthRatio;
}
 
@Override
protected void encode() {
switch (this.mode) {
case MATRIX:
dataMatrix();
break;
case INDUSTRIAL:
industrial();
break;
case IATA:
iata();
break;
case INTERLEAVED:
interleaved(false);
break;
case INTERLEAVED_WITH_CHECK_DIGIT:
interleaved(true);
break;
case DATA_LOGIC:
dataLogic();
break;
case ITF14:
itf14();
break;
case DP_LEITCODE:
deutschePostLeitcode();
break;
case DP_IDENTCODE:
deutschePostIdentcode();
break;
}
}
 
private void dataMatrix() {
 
if (!this.content.matches("[0-9]*")) {
throw new OkapiException("Invalid characters in input");
}
 
String dest = "311111";
for (int i = 0; i < this.content.length(); i++) {
dest += C25_MATRIX_TABLE[Character.getNumericValue(this.content.charAt(i))];
}
dest += "31111";
 
this.readable = this.content;
this.pattern = new String[] { dest };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private void industrial() {
 
if (!this.content.matches("[0-9]*")) {
throw new OkapiException("Invalid characters in input");
}
 
String dest = "313111";
for (int i = 0; i < this.content.length(); i++) {
dest += C25_INDUSTRIAL_TABLE[Character.getNumericValue(this.content.charAt(i))];
}
dest += "31113";
 
this.readable = this.content;
this.pattern = new String[] { dest };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private void iata() {
 
if (!this.content.matches("[0-9]*")) {
throw new OkapiException("Invalid characters in input");
}
 
String dest = "1111";
for (int i = 0; i < this.content.length(); i++) {
dest += C25_INDUSTRIAL_TABLE[Character.getNumericValue(this.content.charAt(i))];
}
dest += "311";
 
this.readable = this.content;
this.pattern = new String[] { dest };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private void dataLogic() {
 
if (!this.content.matches("[0-9]*")) {
throw new OkapiException("Invalid characters in input");
}
 
String dest = "1111";
for (int i = 0; i < this.content.length(); i++) {
dest += C25_MATRIX_TABLE[Character.getNumericValue(this.content.charAt(i))];
}
dest += "311";
 
this.readable = this.content;
this.pattern = new String[] { dest };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private void interleaved(final boolean addCheckDigit) {
int i;
String dest;
 
this.readable = this.content;
 
if (!this.readable.matches("[0-9]*")) {
throw new OkapiException("Invalid characters in input");
}
 
if (addCheckDigit) {
final char checkDigit = checkDigit(this.readable, 1, 3);
this.readable += checkDigit;
infoLine("Check Digit: " + checkDigit);
}
 
if ((this.readable.length() & 1) != 0) {
this.readable = "0" + this.readable;
}
 
dest = "1111";
for (i = 0; i < this.readable.length(); i += 2) {
dest += interlace(i, i + 1);
}
dest += "311";
 
this.pattern = new String[] { dest };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private String interlace(final int x, final int y) {
final char a = this.readable.charAt(x);
final char b = this.readable.charAt(y);
 
final String one = C25_INTERLEAVED_TABLE[Character.getNumericValue(a)];
final String two = C25_INTERLEAVED_TABLE[Character.getNumericValue(b)];
 
final StringBuilder f = new StringBuilder(10);
for (int i = 0; i < 5; i++) {
f.append(one.charAt(i));
f.append(two.charAt(i));
}
 
return f.toString();
}
 
private void itf14() {
int i;
final int input_length = this.content.length();
String dest;
 
if (!this.content.matches("[0-9]*")) {
throw new OkapiException("Invalid characters in input");
}
 
if (input_length > 13) {
throw new OkapiException("Input data too long");
}
 
this.readable = "";
for (i = input_length; i < 13; i++) {
this.readable += "0";
}
this.readable += this.content;
 
final char checkDigit = checkDigit(this.readable, 1, 3);
this.readable += checkDigit;
infoLine("Check Digit: " + checkDigit);
 
dest = "1111";
for (i = 0; i < this.readable.length(); i += 2) {
dest += interlace(i, i + 1);
}
dest += "311";
 
this.pattern = new String[] { dest };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private void deutschePostLeitcode() {
int i;
final int input_length = this.content.length();
String dest;
 
if (!this.content.matches("[0-9]*")) {
throw new OkapiException("Invalid characters in input");
}
 
if (input_length > 13) {
throw new OkapiException("Input data too long");
}
 
this.readable = "";
for (i = input_length; i < 13; i++) {
this.readable += "0";
}
this.readable += this.content;
 
final char checkDigit = checkDigit(this.readable, 9, 4);
this.readable += checkDigit;
infoLine("Check digit: " + checkDigit);
 
dest = "1111";
for (i = 0; i < this.readable.length(); i += 2) {
dest += interlace(i, i + 1);
}
dest += "311";
 
this.pattern = new String[] { dest };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private void deutschePostIdentcode() {
int i;
final int input_length = this.content.length();
String dest;
 
if (!this.content.matches("[0-9]*")) {
throw new OkapiException("Invalid characters in input");
}
 
if (input_length > 11) {
throw new OkapiException("Input data too long");
}
 
this.readable = "";
for (i = input_length; i < 11; i++) {
this.readable += "0";
}
this.readable += this.content;
 
final char checkDigit = checkDigit(this.readable, 9, 4);
this.readable += checkDigit;
infoLine("Check Digit: " + checkDigit);
 
dest = "1111";
for (i = 0; i < this.readable.length(); i += 2) {
dest += interlace(i, i + 1);
}
dest += "311";
 
this.pattern = new String[] { dest };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private static char checkDigit(final String s, final int multiplier1, final int multiplier2) {
int count = 0;
for (int i = s.length() - 1; i >= 0; i--) {
if ((i & 1) != 0) {
count += multiplier1 * (s.charAt(i) - '0');
} else {
count += multiplier2 * (s.charAt(i) - '0');
}
}
return (char) ((10 - count % 10) % 10 + '0');
}
 
@Override
protected void plotSymbol() {
 
int xBlock;
 
this.rectangles.clear();
this.texts.clear();
 
int baseY;
if (this.humanReadableLocation == TOP) {
baseY = getTheoreticalHumanReadableHeight();
} else {
baseY = 0;
}
 
double x = 0;
final int y = baseY;
int h = 0;
boolean black = true;
 
int offset = 0;
if (this.mode == ToFMode.ITF14) {
offset = 20;
}
 
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
final char c = this.pattern[0].charAt(xBlock);
final double w = getModuleWidth(c - '0') * this.moduleWidth;
if (black) {
if (this.row_height[0] == -1) {
h = this.default_height;
} else {
h = this.row_height[0];
}
if (w != 0 && h != 0) {
final Rectangle2D.Double rect = new Rectangle2D.Double(x + offset, y, w, h);
this.rectangles.add(rect);
}
this.symbol_width = (int) Math.ceil(x + w + 2 * offset);
}
black = !black;
x += w;
}
 
this.symbol_height = h;
 
if (this.mode == ToFMode.ITF14) {
// Add bounding box
final Rectangle2D.Double topBar = new Rectangle2D.Double(0, baseY, this.symbol_width, 4);
final Rectangle2D.Double bottomBar = new Rectangle2D.Double(0, baseY + this.symbol_height - 4, this.symbol_width, 4);
final Rectangle2D.Double leftBar = new Rectangle2D.Double(0, baseY, 4, this.symbol_height);
final Rectangle2D.Double rightBar = new Rectangle2D.Double(this.symbol_width - 4, baseY, 4, this.symbol_height);
this.rectangles.add(topBar);
this.rectangles.add(bottomBar);
this.rectangles.add(leftBar);
this.rectangles.add(rightBar);
}
 
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));
}
}
 
/** {@inheritDoc} */
@Override
protected double getModuleWidth(final int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return this.moduleWidthRatio;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/KoreaPost.java
New file
0,0 → 1,64
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
/**
* <p>
* Implements Korea Post Barcode. Input should consist of of a six-digit number. A Modulo-10 check
* digit is calculated and added, and should not form part of the input data.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class KoreaPost extends Symbol {
 
private static final String[] KOREA_TABLE = { "1313150613", "0713131313", "0417131313", "1506131313", "0413171313", "17171313", "1315061313", "0413131713", "17131713", "13171713" };
 
@Override
protected void encode() {
 
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in input");
}
 
if (this.content.length() > 6) {
throw new OkapiException("Input data too long");
}
 
String padded = "";
for (int i = 0; i < 6 - this.content.length(); i++) {
padded += "0";
}
padded += this.content;
 
int total = 0;
String accumulator = "";
for (int i = 0; i < padded.length(); i++) {
final int j = Character.getNumericValue(padded.charAt(i));
accumulator += KOREA_TABLE[j];
total += j;
}
 
int checkd = 10 - total % 10;
if (checkd == 10) {
checkd = 0;
}
infoLine("Check Digit: " + checkd);
accumulator += KOREA_TABLE[checkd];
 
this.readable = padded + checkd;
this.pattern = new String[] { accumulator };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/GridMatrix.java
New file
0,0 → 1,1878
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
 
/**
* <p>
* Implements Grid Matrix bar code symbology according to AIMD014.
*
* <p>
* Grid Matrix is a matrix symbology which can encode characters in the ISO/IEC 8859-1 (Latin-1)
* character set as well as those in the GB-2312 character set. Input is assumed to be formatted as
* a UTF string.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class GridMatrix extends Symbol {
 
private static final char[] SHIFT_SET = {
/* From Table 7 - Encoding of control characters */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*
* NULL
* ->
* SI
*/
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /*
* DLE
* ->
* US
*/
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~' };
 
private static final int[] GM_RECOMMEND_CW = { 9, 30, 59, 114, 170, 237, 315, 405, 506, 618, 741, 875, 1021 };
 
private static final int[] GM_MAX_CW = { 11, 40, 79, 146, 218, 305, 405, 521, 650, 794, 953, 1125, 1313 };
 
private static final int[] GM_DATA_CODEWORDS = { 0, 15, 13, 11, 9, 45, 40, 35, 30, 25, 89, 79, 69, 59, 49, 146, 130, 114, 98, 81, 218, 194, 170, 146, 121, 305, 271, 237, 203, 169, 405, 360, 315,
270, 225, 521, 463, 405, 347, 289, 650, 578, 506, 434, 361, 794, 706, 618, 530, 441, 953, 847, 741, 635, 529, 1125, 1000, 875, 750, 625, 1313, 1167, 1021, 875, 729 };
 
private static final int[] GM_N1 = { 18, 50, 98, 81, 121, 113, 113, 116, 121, 126, 118, 125, 122 };
private static final int[] GM_B1 = { 1, 1, 1, 2, 2, 2, 2, 3, 2, 7, 5, 10, 6 };
private static final int[] GM_B2 = { 0, 0, 0, 0, 0, 1, 2, 2, 4, 0, 4, 0, 6 };
 
private static final int[] GM_EBEB = {
/* E1 B3 E2 B4 */
0, 0, 0, 0, // version 1
3, 1, 0, 0, 5, 1, 0, 0, 7, 1, 0, 0, 9, 1, 0, 0, 5, 1, 0, 0, // version 2
10, 1, 0, 0, 15, 1, 0, 0, 20, 1, 0, 0, 25, 1, 0, 0, 9, 1, 0, 0, // version 3
19, 1, 0, 0, 29, 1, 0, 0, 39, 1, 0, 0, 49, 1, 0, 0, 8, 2, 0, 0, // version 4
16, 2, 0, 0, 24, 2, 0, 0, 32, 2, 0, 0, 41, 1, 10, 1, 12, 2, 0, 0, // version 5
24, 2, 0, 0, 36, 2, 0, 0, 48, 2, 0, 0, 61, 1, 60, 1, 11, 3, 0, 0, // version 6
23, 1, 22, 2, 34, 2, 33, 1, 45, 3, 0, 0, 57, 1, 56, 2, 12, 1, 11, 3, // version 7
23, 2, 22, 2, 34, 3, 33, 1, 45, 4, 0, 0, 57, 1, 56, 3, 12, 2, 11, 3, // version 8
23, 5, 0, 0, 35, 3, 34, 2, 47, 1, 46, 4, 58, 4, 57, 1, 12, 6, 0, 0, // version 9
24, 6, 0, 0, 36, 6, 0, 0, 48, 6, 0, 0, 61, 1, 60, 5, 13, 4, 12, 3, // version 10
26, 1, 25, 6, 38, 5, 37, 2, 51, 2, 50, 5, 63, 7, 0, 0, 12, 6, 11, 3, // version 11
24, 4, 23, 5, 36, 2, 35, 7, 47, 9, 0, 0, 59, 7, 58, 2, 13, 5, 12, 5, // version 12
25, 10, 0, 0, 38, 5, 37, 5, 50, 10, 0, 0, 63, 5, 62, 5, 13, 1, 12, 11, // version 13
25, 3, 24, 9, 37, 5, 36, 7, 49, 7, 48, 5, 61, 9, 60, 3 };
 
private static final int[] GM_MACRO_MATRIX = { 728, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 727, 624, 529,
530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 651, 726, 623, 528, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450,
451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 553, 652, 725, 622, 527, 440, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379,
380, 463, 554, 653, 724, 621, 526, 439, 360, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 381, 464, 555, 654, 723, 620, 525, 438, 359, 288,
225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 307, 382, 465, 556, 655, 722, 619, 524, 437, 358, 287, 224, 169, 170, 171, 172, 173, 174, 175, 176, 177,
178, 179, 180, 181, 182, 241, 308, 383, 466, 557, 656, 721, 618, 523, 436, 357, 286, 223, 168, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 183, 242, 309, 384, 467, 558,
657, 720, 617, 522, 435, 356, 285, 222, 167, 120, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 133, 184, 243, 310, 385, 468, 559, 658, 719, 616, 521, 434, 355, 284, 221, 166, 119, 80, 49, 50,
51, 52, 53, 54, 55, 56, 91, 134, 185, 244, 311, 386, 469, 560, 659, 718, 615, 520, 433, 354, 283, 220, 165, 118, 79, 48, 25, 26, 27, 28, 29, 30, 57, 92, 135, 186, 245, 312, 387, 470, 561,
660, 717, 614, 519, 432, 353, 282, 219, 164, 117, 78, 47, 24, 9, 10, 11, 12, 31, 58, 93, 136, 187, 246, 313, 388, 471, 562, 661, 716, 613, 518, 431, 352, 281, 218, 163, 116, 77, 46, 23, 8,
1, 2, 13, 32, 59, 94, 137, 188, 247, 314, 389, 472, 563, 662, 715, 612, 517, 430, 351, 280, 217, 162, 115, 76, 45, 22, 7, 0, 3, 14, 33, 60, 95, 138, 189, 248, 315, 390, 473, 564, 663, 714,
611, 516, 429, 350, 279, 216, 161, 114, 75, 44, 21, 6, 5, 4, 15, 34, 61, 96, 139, 190, 249, 316, 391, 474, 565, 664, 713, 610, 515, 428, 349, 278, 215, 160, 113, 74, 43, 20, 19, 18, 17,
16, 35, 62, 97, 140, 191, 250, 317, 392, 475, 566, 665, 712, 609, 514, 427, 348, 277, 214, 159, 112, 73, 42, 41, 40, 39, 38, 37, 36, 63, 98, 141, 192, 251, 318, 393, 476, 567, 666, 711,
608, 513, 426, 347, 276, 213, 158, 111, 72, 71, 70, 69, 68, 67, 66, 65, 64, 99, 142, 193, 252, 319, 394, 477, 568, 667, 710, 607, 512, 425, 346, 275, 212, 157, 110, 109, 108, 107, 106,
105, 104, 103, 102, 101, 100, 143, 194, 253, 320, 395, 478, 569, 668, 709, 606, 511, 424, 345, 274, 211, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144, 195, 254, 321,
396, 479, 570, 669, 708, 605, 510, 423, 344, 273, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200, 199, 198, 197, 196, 255, 322, 397, 480, 571, 670, 707, 604, 509, 422, 343, 272,
271, 270, 269, 268, 267, 266, 265, 264, 263, 262, 261, 260, 259, 258, 257, 256, 323, 398, 481, 572, 671, 706, 603, 508, 421, 342, 341, 340, 339, 338, 337, 336, 335, 334, 333, 332, 331,
330, 329, 328, 327, 326, 325, 324, 399, 482, 573, 672, 705, 602, 507, 420, 419, 418, 417, 416, 415, 414, 413, 412, 411, 410, 409, 408, 407, 406, 405, 404, 403, 402, 401, 400, 483, 574,
673, 704, 601, 506, 505, 504, 503, 502, 501, 500, 499, 498, 497, 496, 495, 494, 493, 492, 491, 490, 489, 488, 487, 486, 485, 484, 575, 674, 703, 600, 599, 598, 597, 596, 595, 594, 593,
592, 591, 590, 589, 588, 587, 586, 585, 584, 583, 582, 581, 580, 579, 578, 577, 576, 675, 702, 701, 700, 699, 698, 697, 696, 695, 694, 693, 692, 691, 690, 689, 688, 687, 686, 685, 684,
683, 682, 681, 680, 679, 678, 677, 676 };
 
private static final char[] MIXED_ALPHANUM_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ' ' };
 
private enum Mode {
NULL, GM_NUMBER, GM_LOWER, GM_UPPER, GM_MIXED, GM_CONTROL, GM_BYTE, GM_CHINESE
}
 
private StringBuilder binary;
private final int[] word = new int[1460];
private boolean[] grid;
private Mode appxDnextSection = Mode.NULL;
private Mode appxDlastSection = Mode.NULL;
private int preferredVersion = 0;
private int preferredEccLevel = -1;
 
/**
* Set preferred size, or "version" of the symbol according to the following table. This value
* may be ignored if the data to be encoded does not fit into a symbol of the selected size.
*
* <table summary="Available Grid Matrix symbol sizes">
* <tbody>
* <tr>
* <th>Input</th>
* <th>Size</th>
* </tr>
* <tr>
* <td>1</td>
* <td>18 x 18</td>
* </tr>
* <tr>
* <td>2</td>
* <td>30 x 30</td>
* </tr>
* <tr>
* <td>3</td>
* <td>42 x 42</td>
* </tr>
* <tr>
* <td>4</td>
* <td>54 x 54</td>
* </tr>
* <tr>
* <td>5</td>
* <td>66 x 66</td>
* </tr>
* <tr>
* <td>6</td>
* <td>78 x 78</td>
* </tr>
* <tr>
* <td>7</td>
* <td>90 x 90</td>
* </tr>
* <tr>
* <td>8</td>
* <td>102 x 102</td>
* </tr>
* <tr>
* <td>9</td>
* <td>114 x 114</td>
* </tr>
* <tr>
* <td>10</td>
* <td>126 x 126</td>
* </tr>
* <tr>
* <td>11</td>
* <td>138 x 138</td>
* </tr>
* <tr>
* <td>12</td>
* <td>150 x 150</td>
* </tr>
* <tr>
* <td>13</td>
* <td>162 x 162</td>
* </tr>
* </tbody>
* </table>
*
* @param version symbol version
*/
public void setPreferredVersion(final int version) {
this.preferredVersion = version;
}
 
/**
* Set the preferred amount of the symbol which should be dedicated to error correction data.
* Values should be selected from the following table:
*
* <table summary="Available options for error correction capacity">
* <tbody>
* <tr>
* <th>Mode</th>
* <th>Error Correction Capacity</th>
* </tr>
* <tr>
* <td>1</td>
* <td>Approximately 10%</td>
* </tr>
* <tr>
* <td>2</td>
* <td>Approximately 20%</td>
* </tr>
* <tr>
* <td>3</td>
* <td>Approximately 30%</td>
* </tr>
* <tr>
* <td>4</td>
* <td>Approximately 40%</td>
* </tr>
* <tr>
* <td>5</td>
* <td>Approximately 50%</td>
* </tr>
* </tbody>
* </table>
*
* @param eccLevel error correction level
*/
public void setPreferredEccLevel(final int eccLevel) {
this.preferredEccLevel = eccLevel;
}
 
@Override
protected void encode() {
int size, modules, dark, error_number;
int auto_layers, min_layers, layers, auto_ecc_level, min_ecc_level, ecc_level;
int x, y, i;
int data_cw, input_latch = 0;
int data_max;
int length;
final StringBuilder bin = new StringBuilder();
 
for (i = 0; i < 1460; i++) {
this.word[i] = 0;
}
 
try {
final Charset gb2312 = Charset.forName("GB2312");
if (gb2312.newEncoder().canEncode(this.content)) {
/* GB2312 will work, use Chinese compaction */
final byte[] inputBytes = this.content.getBytes(gb2312);
this.inputData = new int[inputBytes.length];
length = 0;
for (i = 0; i < inputBytes.length; i++) {
if ((inputBytes[i] & 0xFF) >= 0xA1 && (inputBytes[i] & 0xFF) <= 0xF7) {
/* Double byte character */
this.inputData[length] = (inputBytes[i] & 0xFF) * 256 + (inputBytes[i + 1] & 0xFF);
i++;
length++;
} else {
/* Single byte character */
this.inputData[length] = inputBytes[i] & 0xFF;
length++;
}
}
infoLine("Using GB2312 character encoding");
this.eciMode = 29;
} else {
/* GB2312 encoding won't work, use other ECI mode */
eciProcess(); // Get ECI mode
length = this.inputData.length;
}
} catch (final UnsupportedCharsetException e) {
throw new OkapiException("Byte conversion encoding error");
}
 
error_number = encodeGridMatrixBinary(length, this.readerInit);
if (error_number != 0) {
throw new OkapiException("Input data too long");
}
 
/* Determine the size of the symbol */
data_cw = this.binary.length() / 7;
 
auto_layers = 1;
for (i = 0; i < 13; i++) {
if (GM_RECOMMEND_CW[i] < data_cw) {
auto_layers = i + 1;
}
}
 
min_layers = 13;
for (i = 12; i > 0; i--) {
if (GM_MAX_CW[i - 1] >= data_cw) {
min_layers = i;
}
}
layers = auto_layers;
auto_ecc_level = 3;
if (layers == 1) {
auto_ecc_level = 5;
}
if (layers == 2 || layers == 3) {
auto_ecc_level = 4;
}
min_ecc_level = 1;
if (layers == 1) {
min_ecc_level = 4;
}
if (layers == 2 || layers == 3) {
min_ecc_level = 2;
}
ecc_level = auto_ecc_level;
 
if (this.preferredVersion >= 1 && this.preferredVersion <= 13) {
input_latch = 1;
if (this.preferredVersion > min_layers) {
layers = this.preferredVersion;
} else {
layers = min_layers;
}
}
 
if (input_latch == 1) {
auto_ecc_level = 3;
if (layers == 1) {
auto_ecc_level = 5;
}
if (layers == 2 || layers == 3) {
auto_ecc_level = 4;
}
ecc_level = auto_ecc_level;
if (data_cw > GM_DATA_CODEWORDS[5 * (layers - 1) + ecc_level - 1]) {
layers++;
}
}
 
if (input_latch == 0) {
if (this.preferredEccLevel >= 1 && this.preferredEccLevel <= 5) {
if (this.preferredEccLevel > min_ecc_level) {
ecc_level = this.preferredEccLevel;
} else {
ecc_level = min_ecc_level;
}
}
if (data_cw > GM_DATA_CODEWORDS[5 * (layers - 1) + ecc_level - 1]) {
do {
layers++;
} while (data_cw > GM_DATA_CODEWORDS[5 * (layers - 1) + ecc_level - 1] && layers <= 13);
}
}
 
data_max = 1313;
switch (ecc_level) {
case 2:
data_max = 1167;
break;
case 3:
data_max = 1021;
break;
case 4:
data_max = 875;
break;
case 5:
data_max = 729;
break;
}
 
if (data_cw > data_max) {
throw new OkapiException("Input data too long");
}
 
addErrorCorrection(data_cw, layers, ecc_level);
size = 6 + layers * 12;
modules = 1 + layers * 2;
 
infoLine("Layers: " + layers);
infoLine("ECC Level: " + ecc_level);
infoLine("Data Codewords: " + data_cw);
infoLine("ECC Codewords: " + GM_DATA_CODEWORDS[(layers - 1) * 5 + ecc_level - 1]);
infoLine("Grid Size: " + modules + " X " + modules);
 
this.grid = new boolean[size * size];
 
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
this.grid[y * size + x] = false;
}
}
 
placeDataInGrid(modules, size);
addLayerId(size, layers, modules, ecc_level);
 
/* Add macromodule frames */
for (x = 0; x < modules; x++) {
dark = 1 - (x & 1);
for (y = 0; y < modules; y++) {
if (dark == 1) {
for (i = 0; i < 5; i++) {
this.grid[y * 6 * size + x * 6 + i] = true;
this.grid[(y * 6 + 5) * size + x * 6 + i] = true;
this.grid[(y * 6 + i) * size + x * 6] = true;
this.grid[(y * 6 + i) * size + x * 6 + 5] = true;
}
this.grid[(y * 6 + 5) * size + x * 6 + 5] = true;
dark = 0;
} else {
dark = 1;
}
}
}
 
/* Copy values to symbol */
this.symbol_width = size;
this.row_count = size;
this.row_height = new int[this.row_count];
this.pattern = new String[this.row_count];
 
for (x = 0; x < size; x++) {
bin.setLength(0);
for (y = 0; y < size; y++) {
if (this.grid[x * size + y]) {
bin.append('1');
} else {
bin.append('0');
}
}
this.row_height[x] = 1;
this.pattern[x] = bin2pat(bin);
}
}
 
private int encodeGridMatrixBinary(final int length, final boolean reader) {
/*
* Create a binary stream representation of the input data. 7 sets are defined - Chinese
* characters, Numerals, Lower case letters, Upper case letters, Mixed numerals and letters,
* Control characters and 8-bit binary data
*/
int sp, glyph = 0;
Mode current_mode, next_mode, last_mode;
int c1, c2;
boolean done;
int p = 0, ppos;
int punt = 0;
int number_pad_posn;
int byte_count_posn = 0, byte_count = 0;
int shift, i;
final int[] numbuf = new int[3];
final Mode[] modeMap = calculateModeMap(length);
 
this.binary = new StringBuilder();
 
sp = 0;
current_mode = Mode.NULL;
number_pad_posn = 0;
 
info("Encoding: ");
 
if (reader) {
this.binary.append("1010"); /* FNC3 - Reader Initialisation */
info("INIT ");
}
 
if (this.eciMode != 3 && this.eciMode != 29) {
this.binary.append("1100"); /* ECI */
 
if (this.eciMode >= 0 && this.eciMode <= 1023) {
this.binary.append('0');
for (i = 0x200; i > 0; i = i >> 1) {
if ((this.eciMode & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
}
 
if (this.eciMode >= 1024 && this.eciMode <= 32767) {
this.binary.append("10");
for (i = 0x4000; i > 0; i = i >> 1) {
if ((this.eciMode & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
}
 
if (this.eciMode >= 32768 && this.eciMode <= 811799) {
this.binary.append("11");
for (i = 0x80000; i > 0; i = i >> 1) {
if ((this.eciMode & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
}
 
info("ECI ");
infoSpace(this.eciMode);
}
 
do {
next_mode = modeMap[sp];
 
if (next_mode != current_mode) {
switch (current_mode) {
case NULL:
switch (next_mode) {
case GM_CHINESE:
this.binary.append("0001");
break;
case GM_NUMBER:
this.binary.append("0010");
break;
case GM_LOWER:
this.binary.append("0011");
break;
case GM_UPPER:
this.binary.append("0100");
break;
case GM_MIXED:
this.binary.append("0101");
break;
case GM_BYTE:
this.binary.append("0111");
break;
}
break;
case GM_CHINESE:
switch (next_mode) {
case GM_NUMBER:
this.binary.append("1111111100001");
break; // 8161
case GM_LOWER:
this.binary.append("1111111100010");
break; // 8162
case GM_UPPER:
this.binary.append("1111111100011");
break; // 8163
case GM_MIXED:
this.binary.append("1111111100100");
break; // 8164
case GM_BYTE:
this.binary.append("1111111100101");
break; // 8165
}
break;
case GM_NUMBER:
/* add numeric block padding value */
switch (p) {
case 1:
this.binary.insert(number_pad_posn, "10");
break; // 2 pad digits
case 2:
this.binary.insert(number_pad_posn, "01");
break; // 1 pad digit
case 3:
this.binary.insert(number_pad_posn, "00");
break; // 0 pad digits
}
 
switch (next_mode) {
case GM_CHINESE:
this.binary.append("1111111011");
break; // 1019
case GM_LOWER:
this.binary.append("1111111100");
break; // 1020
case GM_UPPER:
this.binary.append("1111111101");
break; // 1021
case GM_MIXED:
this.binary.append("1111111110");
break; // 1022
case GM_BYTE:
this.binary.append("1111111111");
break; // 1023
}
break;
case GM_LOWER:
case GM_UPPER:
switch (next_mode) {
case GM_CHINESE:
this.binary.append("11100");
break; // 28
case GM_NUMBER:
this.binary.append("11101");
break; // 29
case GM_LOWER:
case GM_UPPER:
this.binary.append("11110");
break; // 30
case GM_MIXED:
this.binary.append("1111100");
break; // 124
case GM_BYTE:
this.binary.append("1111110");
break; // 126
}
break;
case GM_MIXED:
switch (next_mode) {
case GM_CHINESE:
this.binary.append("1111110001");
break; // 1009
case GM_NUMBER:
this.binary.append("1111110010");
break; // 1010
case GM_LOWER:
this.binary.append("1111110011");
break; // 1011
case GM_UPPER:
this.binary.append("1111110100");
break; // 1012
case GM_BYTE:
this.binary.append("1111110111");
break; // 1015
}
break;
case GM_BYTE:
/* add byte block length indicator */
addByteCount(byte_count_posn, byte_count);
byte_count = 0;
switch (next_mode) {
case GM_CHINESE:
this.binary.append("0001");
break; // 1
case GM_NUMBER:
this.binary.append("0010");
break; // 2
case GM_LOWER:
this.binary.append("0011");
break; // 3
case GM_UPPER:
this.binary.append("0100");
break; // 4
case GM_MIXED:
this.binary.append("0101");
break; // 5
}
break;
}
 
switch (next_mode) {
case GM_CHINESE:
info("CHIN ");
break;
case GM_NUMBER:
info("NUMB ");
break;
case GM_LOWER:
info("LOWR ");
break;
case GM_UPPER:
info("UPPR ");
break;
case GM_MIXED:
info("MIXD ");
break;
case GM_BYTE:
info("BYTE ");
break;
}
 
}
last_mode = current_mode;
current_mode = next_mode;
 
switch (current_mode) {
case GM_CHINESE:
done = false;
if (this.inputData[sp] > 0xff) {
/* GB2312 character */
c1 = (this.inputData[sp] & 0xff00) >> 8;
c2 = this.inputData[sp] & 0xff;
 
if (c1 >= 0xa0 && c1 <= 0xa9) {
glyph = 0x60 * (c1 - 0xa1) + c2 - 0xa0;
}
if (c1 >= 0xb0 && c1 <= 0xf7) {
glyph = 0x60 * (c1 - 0xb0 + 9) + c2 - 0xa0;
}
done = true;
}
if (!done) {
if (sp != length - 1) {
if (this.inputData[sp] == 13 && this.inputData[sp + 1] == 10) {
/* End of Line */
glyph = 7776;
sp++;
}
done = true;
}
}
if (!done) {
if (sp != length - 1) {
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9' && this.inputData[sp + 1] >= '0' && this.inputData[sp + 1] <= '9') {
/* Two digits */
glyph = 8033 + 10 * (this.inputData[sp] - '0') + this.inputData[sp + 1] - '0';
sp++;
}
}
}
if (!done) {
/* Byte value */
glyph = 7777 + this.inputData[sp];
}
 
infoSpace(glyph);
 
for (i = 0x1000; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
sp++;
break;
 
case GM_NUMBER:
if (last_mode != current_mode) {
/* Reserve a space for numeric digit padding value (2 bits) */
number_pad_posn = this.binary.length();
}
p = 0;
ppos = -1;
 
/*
* Numeric compression can also include certain combinations of non-numeric
* character
*/
numbuf[0] = '0';
numbuf[1] = '0';
numbuf[2] = '0';
do {
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') {
numbuf[p] = this.inputData[sp];
p++;
}
switch (this.inputData[sp]) {
case ' ':
case '+':
case '-':
case '.':
case ',':
punt = this.inputData[sp];
ppos = p;
break;
}
if (sp < length - 1) {
if (this.inputData[sp] == 13 && this.inputData[sp + 1] == 10) {
/* <end of line> */
punt = this.inputData[sp];
sp++;
ppos = p;
}
}
sp++;
} while (p < 3 && sp < length);
 
if (ppos != -1) {
switch (punt) {
case ' ':
glyph = 0;
break;
case '+':
glyph = 3;
break;
case '-':
glyph = 6;
break;
case '.':
glyph = 9;
break;
case ',':
glyph = 12;
break;
case 0x13:
glyph = 15;
break;
}
glyph += ppos;
glyph += 1000;
 
infoSpace(glyph);
 
for (i = 0x200; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
}
 
glyph = 100 * (numbuf[0] - '0') + 10 * (numbuf[1] - '0') + numbuf[2] - '0';
infoSpace(glyph);
 
for (i = 0x200; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
break;
 
case GM_BYTE:
if (last_mode != current_mode) {
/* Reserve space for byte block length indicator (9 bits) */
byte_count_posn = this.binary.length();
}
if (byte_count == 512) {
/*
* Maximum byte block size is 512 bytes. If longer is needed then start a new
* block
*/
addByteCount(byte_count_posn, byte_count);
this.binary.append("0111");
byte_count_posn = this.binary.length();
byte_count = 0;
}
 
glyph = this.inputData[sp];
infoSpace(glyph);
for (i = 0x80; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
sp++;
byte_count++;
break;
 
case GM_MIXED:
shift = 1;
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') {
shift = 0;
}
if (this.inputData[sp] >= 'A' && this.inputData[sp] <= 'Z') {
shift = 0;
}
if (this.inputData[sp] >= 'a' && this.inputData[sp] <= 'z') {
shift = 0;
}
if (this.inputData[sp] == ' ') {
shift = 0;
}
 
if (shift == 0) {
/* Mixed Mode character */
glyph = positionOf((char) this.inputData[sp], MIXED_ALPHANUM_SET);
infoSpace(glyph);
 
for (i = 0x20; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
} else {
/* Shift Mode character */
this.binary.append("1111110110"); /* 1014 - shift indicator */
 
addShiftCharacter(this.inputData[sp]);
}
 
sp++;
break;
 
case GM_UPPER:
shift = 1;
if (this.inputData[sp] >= 'A' && this.inputData[sp] <= 'Z') {
shift = 0;
}
if (this.inputData[sp] == ' ') {
shift = 0;
}
 
if (shift == 0) {
/* Upper Case character */
glyph = positionOf((char) this.inputData[sp], MIXED_ALPHANUM_SET) - 10;
if (glyph == 52) {
// Space character
glyph = 26;
}
infoSpace(glyph);
 
for (i = 0x10; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
 
} else {
/* Shift Mode character */
this.binary.append("1111101"); /* 127 - shift indicator */
 
addShiftCharacter(this.inputData[sp]);
}
 
sp++;
break;
 
case GM_LOWER:
shift = 1;
if (this.inputData[sp] >= 'a' && this.inputData[sp] <= 'z') {
shift = 0;
}
if (this.inputData[sp] == ' ') {
shift = 0;
}
 
if (shift == 0) {
/* Lower Case character */
glyph = positionOf((char) this.inputData[sp], MIXED_ALPHANUM_SET) - 36;
infoSpace(glyph);
 
for (i = 0x10; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
 
} else {
/* Shift Mode character */
this.binary.append("1111101"); /* 127 - shift indicator */
 
addShiftCharacter(this.inputData[sp]);
}
 
sp++;
break;
}
if (this.binary.length() > 9191) {
return 1;
}
 
} while (sp < length);
 
infoLine();
 
if (current_mode == Mode.GM_NUMBER) {
/* add numeric block padding value */
switch (p) {
case 1:
this.binary.insert(number_pad_posn, "10");
break; // 2 pad digits
case 2:
this.binary.insert(number_pad_posn, "01");
break; // 1 pad digit
case 3:
this.binary.insert(number_pad_posn, "00");
break; // 0 pad digits
}
}
 
if (current_mode == Mode.GM_BYTE) {
/* Add byte block length indicator */
addByteCount(byte_count_posn, byte_count);
}
 
/* Add "end of data" character */
switch (current_mode) {
case GM_CHINESE:
this.binary.append("1111111100000");
break; // 8160
case GM_NUMBER:
this.binary.append("1111111010");
break; // 1018
case GM_LOWER:
case GM_UPPER:
this.binary.append("11011");
break; // 27
case GM_MIXED:
this.binary.append("1111110000");
break; // 1008
case GM_BYTE:
this.binary.append("0000");
break; // 0
}
 
/* Add padding bits if required */
p = 7 - this.binary.length() % 7;
if (p == 7) {
p = 0;
}
for (i = 0; i < p; i++) {
this.binary.append('0');
}
 
if (this.binary.length() > 9191) {
return 1;
}
 
return 0;
}
 
private Mode[] calculateModeMap(final int length) {
final Mode[] modeMap = new Mode[length];
int i;
int digitStart, digitLength;
boolean digits;
int spaceStart, spaceLength;
boolean spaces;
int[] segmentLength;
Mode[] segmentType;
int[] segmentStart;
int segmentCount;
 
// Step 1
// Characters in GB2312 are encoded as Chinese characters
for (i = 0; i < length; i++) {
modeMap[i] = Mode.NULL;
if (this.inputData[i] > 0xFF) {
modeMap[i] = Mode.GM_CHINESE;
}
}
 
// Consecutive <end of line> characters, if preceeded by or followed
// by chinese characters, are encoded as chinese characters.
if (length > 3) {
i = 1;
do {
if (this.inputData[i] == 13 && this.inputData[i + 1] == 10) {
// End of line (CR/LF)
 
if (modeMap[i - 1] == Mode.GM_CHINESE) {
modeMap[i] = Mode.GM_CHINESE;
modeMap[i + 1] = Mode.GM_CHINESE;
}
i += 2;
} else {
i++;
}
} while (i < length - 1);
 
i = length - 3;
do {
if (this.inputData[i] == 13 && this.inputData[i + 1] == 10) {
// End of line (CR/LF)
if (modeMap[i + 2] == Mode.GM_CHINESE) {
modeMap[i] = Mode.GM_CHINESE;
modeMap[i + 1] = Mode.GM_CHINESE;
}
i -= 2;
} else {
i--;
}
} while (i > 0);
}
 
// Digit pairs between chinese characters encode as chinese characters.
digits = false;
digitLength = 0;
digitStart = 0;
for (i = 1; i < length - 1; i++) {
if (this.inputData[i] >= 48 && this.inputData[i] <= 57) {
// '0' to '9'
if (digits == false) {
digits = true;
digitLength = 1;
digitStart = i;
} else {
digitLength++;
}
} else {
if (digits == true) {
if (digitLength % 2 == 0) {
if (modeMap[digitStart - 1] == Mode.GM_CHINESE && modeMap[i] == Mode.GM_CHINESE) {
for (int j = 0; j < digitLength; j++) {
modeMap[i - j - 1] = Mode.GM_CHINESE;
}
}
}
digits = false;
}
}
}
 
// Step 2: all characters 'a' to 'z' are lowercase.
for (i = 0; i < length; i++) {
if (this.inputData[i] >= 97 && this.inputData[i] <= 122) {
modeMap[i] = Mode.GM_LOWER;
}
}
 
// Step 3: all characters 'A' to 'Z' are uppercase.
for (i = 0; i < length; i++) {
if (this.inputData[i] >= 65 && this.inputData[i] <= 90) {
modeMap[i] = Mode.GM_UPPER;
}
}
 
// Step 4: find consecutive <space> characters preceeded or followed
// by uppercase or lowercase.
spaces = false;
spaceLength = 0;
spaceStart = 0;
for (i = 1; i < length - 1; i++) {
if (this.inputData[i] == 32) {
if (spaces == false) {
spaces = true;
spaceLength = 1;
spaceStart = i;
} else {
spaceLength++;
}
} else {
if (spaces == true) {
 
final Mode modeX = modeMap[spaceStart - 1];
final Mode modeY = modeMap[i];
 
if (modeX == Mode.GM_LOWER || modeX == Mode.GM_UPPER) {
for (int j = 0; j < spaceLength; j++) {
modeMap[i - j - 1] = modeX;
}
} else {
if (modeY == Mode.GM_LOWER || modeY == Mode.GM_UPPER) {
for (int j = 0; j < spaceLength; j++) {
modeMap[i - j - 1] = modeY;
}
}
}
spaces = false;
}
}
}
 
// Step 5: Unassigned characters '0' to '9' are assigned as numerals.
// Non-numeric characters in table 7 are also assigned as numerals.
for (i = 0; i < length; i++) {
if (modeMap[i] == Mode.NULL) {
if (this.inputData[i] >= 48 && this.inputData[i] <= 57) {
// '0' to '9'
modeMap[i] = Mode.GM_NUMBER;
} else {
switch (this.inputData[i]) {
case 32: // Space
case 43: // '+'
case 45: // '-'
case 46: // "."
case 44: // ","
modeMap[i] = Mode.GM_NUMBER;
break;
case 13: // CR
if (i < length - 1) {
if (this.inputData[i + 1] == 10) { // LF
// <end of line>
modeMap[i] = Mode.GM_NUMBER;
modeMap[i + 1] = Mode.GM_NUMBER;
}
}
}
}
}
}
 
// Step 6: The remining unassigned bytes are assigned as 8-bit binary
for (i = 0; i < length; i++) {
if (modeMap[i] == Mode.NULL) {
modeMap[i] = Mode.GM_BYTE;
}
}
 
// break into segments
segmentLength = new int[length];
segmentType = new Mode[length];
segmentStart = new int[length];
 
segmentCount = 0;
segmentLength[0] = 1;
segmentType[0] = modeMap[0];
segmentStart[0] = 0;
for (i = 1; i < length; i++) {
if (modeMap[i] == modeMap[i - 1]) {
segmentLength[segmentCount]++;
} else {
segmentCount++;
segmentLength[segmentCount] = 1;
segmentType[segmentCount] = modeMap[i];
segmentStart[segmentCount] = i;
}
}
 
// A segment can be a control segment if
// a) It is not the start segment of the data stream
// b) All characters are control characters
// c) The length of the segment is no more than 3
// d) The previous segment is not chinese
if (segmentCount > 1) {
for (i = 1; i < segmentCount; i++) { // (a)
if (segmentLength[i] <= 3 && segmentType[i - 1] != Mode.GM_CHINESE) { // (c) and (d)
boolean controlLatch = true;
for (int j = 0; j < segmentLength[i]; j++) {
boolean thischarLatch = false;
for (int k = 0; k < 63; k++) {
if (this.inputData[segmentStart[i] + j] == SHIFT_SET[k]) {
thischarLatch = true;
}
}
 
if (!thischarLatch) {
// This character is not a control character
controlLatch = false;
}
}
 
if (controlLatch) { // (b)
segmentType[i] = Mode.GM_CONTROL;
}
}
}
}
 
// Stages 7 to 9
if (segmentCount >= 3) {
for (i = 0; i < segmentCount - 1; i++) {
Mode pm, tm, nm, lm;
int tl, nl, ll, position;
boolean lastSegment = false;
 
if (i == 0) {
pm = Mode.NULL;
} else {
pm = segmentType[i - 1];
}
 
tm = segmentType[i];
tl = segmentLength[i];
 
nm = segmentType[i + 1];
nl = segmentLength[i + 1];
 
lm = segmentType[i + 2];
ll = segmentLength[i + 2];
 
position = segmentStart[i];
 
if (i + 2 == segmentCount) {
lastSegment = true;
}
 
segmentType[i] = getBestMode(pm, tm, nm, lm, tl, nl, ll, position, lastSegment);
 
if (segmentType[i] == Mode.GM_CONTROL) {
segmentType[i] = segmentType[i - 1];
}
}
 
segmentType[i] = this.appxDnextSection;
segmentType[i + 1] = this.appxDlastSection;
 
if (segmentType[i] == Mode.GM_CONTROL) {
segmentType[i] = segmentType[i - 1];
}
if (segmentType[i + 1] == Mode.GM_CONTROL) {
segmentType[i + 1] = segmentType[i];
}
 
// Uncomment these lines to override mode selection and generate symbol as shown
// in image D.1 for the test data "AAT2556 电池充电器+降压转换器 200mA至2A tel:86 019 82512738"
// segmentType[9] = gmMode.GM_LOWER;
// segmentType[10] = gmMode.GM_LOWER;
}
 
// Copy segments back to modeMap
for (i = 0; i < segmentCount; i++) {
for (int j = 0; j < segmentLength[i]; j++) {
modeMap[segmentStart[i] + j] = segmentType[i];
}
}
 
return modeMap;
}
 
private boolean isTransitionValid(final Mode previousMode, final Mode thisMode) {
// Filters possible encoding data types from table D.1
boolean isValid = false;
 
switch (previousMode) {
case GM_CHINESE:
switch (thisMode) {
case GM_CHINESE:
case GM_BYTE:
isValid = true;
break;
}
break;
case GM_NUMBER:
switch (thisMode) {
case GM_NUMBER:
case GM_MIXED:
case GM_BYTE:
case GM_CHINESE:
isValid = true;
break;
}
break;
case GM_LOWER:
switch (thisMode) {
case GM_LOWER:
case GM_MIXED:
case GM_BYTE:
case GM_CHINESE:
isValid = true;
break;
}
break;
case GM_UPPER:
switch (thisMode) {
case GM_UPPER:
case GM_MIXED:
case GM_BYTE:
case GM_CHINESE:
isValid = true;
break;
}
break;
case GM_CONTROL:
switch (thisMode) {
case GM_CONTROL:
case GM_BYTE:
case GM_CHINESE:
isValid = true;
break;
}
break;
case GM_BYTE:
switch (thisMode) {
case GM_BYTE:
case GM_CHINESE:
isValid = true;
break;
}
break;
}
 
return isValid;
}
 
private Mode intToMode(final int input) {
Mode retVal;
 
switch (input) {
case 1:
retVal = Mode.GM_CHINESE;
break;
case 2:
retVal = Mode.GM_BYTE;
break;
case 3:
retVal = Mode.GM_CONTROL;
break;
case 4:
retVal = Mode.GM_MIXED;
break;
case 5:
retVal = Mode.GM_UPPER;
break;
case 6:
retVal = Mode.GM_LOWER;
break;
case 7:
retVal = Mode.GM_NUMBER;
break;
default:
retVal = Mode.NULL;
break;
}
 
return retVal;
}
 
private Mode getBestMode(final Mode pm, final Mode tm, final Mode nm, final Mode lm, final int tl, final int nl, final int ll, final int position, final boolean lastSegment) {
int tmi, nmi, lmi;
Mode bestMode = tm;
int binaryLength;
int bestBinaryLength = Integer.MAX_VALUE;
 
for (tmi = 1; tmi < 8; tmi++) {
if (isTransitionValid(tm, intToMode(tmi))) {
for (nmi = 1; nmi < 8; nmi++) {
if (isTransitionValid(nm, intToMode(nmi))) {
for (lmi = 1; lmi < 8; lmi++) {
if (isTransitionValid(lm, intToMode(lmi))) {
binaryLength = getBinaryLength(pm, intToMode(tmi), intToMode(nmi), intToMode(lmi), tl, nl, ll, position, lastSegment);
if (binaryLength <= bestBinaryLength) {
bestMode = intToMode(tmi);
this.appxDnextSection = intToMode(nmi);
this.appxDlastSection = intToMode(lmi);
bestBinaryLength = binaryLength;
}
}
}
}
}
}
}
 
return bestMode;
}
 
private int getBinaryLength(final Mode pm, final Mode tm, final Mode nm, final Mode lm, final int tl, final int nl, final int ll, final int position, final boolean lastSegment) {
 
int binaryLength = getChunkLength(pm, tm, tl, position);
binaryLength += getChunkLength(tm, nm, nl, position + tl);
binaryLength += getChunkLength(nm, lm, ll, position + tl + nl);
 
if (lastSegment) {
switch (lm) {
case GM_CHINESE:
binaryLength += 13;
break;
case GM_NUMBER:
binaryLength += 10;
break;
case GM_LOWER:
case GM_UPPER:
binaryLength += 5;
break;
case GM_MIXED:
binaryLength += 10;
break;
case GM_BYTE:
binaryLength += 4;
break;
}
}
 
return binaryLength;
}
 
private int getChunkLength(final Mode lastMode, final Mode thisMode, final int thisLength, final int position) {
int byteLength;
 
switch (thisMode) {
case GM_CHINESE:
byteLength = calcChineseLength(position, thisLength);
break;
case GM_NUMBER:
byteLength = calcNumberLength(position, thisLength);
break;
case GM_LOWER:
byteLength = 5 * thisLength;
break;
case GM_UPPER:
byteLength = 5 * thisLength;
break;
case GM_MIXED:
byteLength = calcMixedLength(position, thisLength);
break;
case GM_CONTROL:
byteLength = 6 * thisLength;
break;
default:
// case GM_BYTE:
byteLength = calcByteLength(position, thisLength);
break;
}
 
switch (lastMode) {
case NULL:
byteLength += 4;
break;
case GM_CHINESE:
if (thisMode != Mode.GM_CHINESE && thisMode != Mode.GM_CONTROL) {
byteLength += 13;
}
break;
case GM_NUMBER:
if (thisMode != Mode.GM_CHINESE && thisMode != Mode.GM_CONTROL) {
byteLength += 10;
}
break;
case GM_LOWER:
switch (thisMode) {
case GM_CHINESE:
case GM_NUMBER:
case GM_UPPER:
byteLength += 5;
break;
case GM_MIXED:
case GM_CONTROL:
case GM_BYTE:
byteLength += 7;
break;
}
break;
case GM_UPPER:
switch (thisMode) {
case GM_CHINESE:
case GM_NUMBER:
case GM_LOWER:
byteLength += 5;
break;
case GM_MIXED:
case GM_CONTROL:
case GM_BYTE:
byteLength += 7;
break;
}
break;
case GM_MIXED:
if (thisMode != Mode.GM_MIXED) {
byteLength += 10;
}
break;
case GM_BYTE:
if (thisMode != Mode.GM_BYTE) {
byteLength += 4;
}
break;
}
 
if (lastMode != Mode.GM_BYTE && thisMode == Mode.GM_BYTE) {
byteLength += 9;
}
 
if (lastMode != Mode.GM_NUMBER && thisMode == Mode.GM_NUMBER) {
byteLength += 2;
}
 
return byteLength;
}
 
private int calcChineseLength(final int position, final int length) {
int i = 0;
int bits = 0;
 
do {
bits += 13;
 
if (i < length) {
if (this.inputData[position + i] == 13 && this.inputData[position + i + 1] == 10) {
// <end of line>
i++;
}
 
if (this.inputData[position + i] >= 48 && this.inputData[position + i] <= 57 && this.inputData[position + i + 1] >= 48 && this.inputData[position + i + 1] <= 57) {
// two digits
i++;
}
}
i++;
} while (i < length);
 
return bits;
}
 
private int calcMixedLength(final int position, final int length) {
int bits = 0;
int i;
 
for (i = 0; i < length; i++) {
bits += 6;
for (int k = 0; k < 63; k++) {
if (this.inputData[position + i] == SHIFT_SET[k]) {
bits += 10;
}
}
}
 
return bits;
}
 
private int calcNumberLength(final int position, final int length) {
int i;
int bits = 0;
int numbers = 0;
int nonnumbers = 0;
 
for (i = 0; i < length; i++) {
if (this.inputData[position + i] >= 48 && this.inputData[position + i] <= 57) {
numbers++;
} else {
nonnumbers++;
}
 
if (i != 0) {
if (this.inputData[position + i] == 10 && this.inputData[position + i - 1] == 13) {
// <end of line>
nonnumbers--;
}
}
 
if (numbers == 3) {
if (nonnumbers == 1) {
bits += 20;
} else {
bits += 10;
}
if (nonnumbers > 1) {
// Invalid encoding
bits += 100;
}
numbers = 0;
nonnumbers = 0;
}
}
 
if (numbers > 0) {
if (nonnumbers == 1) {
bits += 20;
} else {
bits += 10;
}
}
 
if (nonnumbers > 1) {
// Invalid
bits += 100;
}
 
if (!(this.inputData[position + i - 1] >= 48 && this.inputData[position + i - 1] <= 57)) {
// Data must end with a digit
bits += 100;
}
 
return bits;
}
 
private int calcByteLength(final int position, final int length) {
int i;
int bits = 0;
 
for (i = 0; i < length; i++) {
if (this.inputData[position + i] <= 0xFF) {
bits += 8;
} else {
bits += 16;
}
}
 
return bits;
}
 
private void addByteCount(final int byte_count_posn, final int byte_count) {
/* Add the length indicator for byte encoded blocks */
for (int i = 0; i < 9; i++) {
if ((byte_count & 0x100 >> i) != 0) {
this.binary.insert(byte_count_posn + i, '0');
} else {
this.binary.insert(byte_count_posn + i, '1');
}
}
}
 
void addShiftCharacter(final int shifty) {
/* Add a control character to the data stream */
int i;
int glyph = 0;
 
for (i = 0; i < 64; i++) {
if (SHIFT_SET[i] == shifty) {
glyph = i;
}
}
 
info("SHT/");
infoSpace(glyph);
 
for (i = 0x20; i > 0; i = i >> 1) {
if ((glyph & i) != 0) {
this.binary.append('1');
} else {
this.binary.append('0');
}
}
}
 
private void addErrorCorrection(final int data_posn, final int layers, final int ecc_level) {
int data_cw, i, j, wp;
int n1, b1, n2, b2, e1, b3, e2;
int block_size, data_size, ecc_size;
final int[] data = new int[1320];
final int[] block = new int[130];
final int[] data_block = new int[115];
final int[] ecc_block = new int[70];
final ReedSolomon rs = new ReedSolomon();
 
data_cw = GM_DATA_CODEWORDS[(layers - 1) * 5 + ecc_level - 1];
 
for (i = 0; i < 1320; i++) {
data[i] = 0;
}
 
/* Convert from binary sream to 7-bit codewords */
for (i = 0; i < data_posn; i++) {
for (j = 0; j < 7; j++) {
if (this.binary.charAt(i * 7 + j) == '1') {
data[i] += 0x40 >> j;
}
}
}
 
info("Codewords: ");
for (i = 0; i < data_posn; i++) {
infoSpace(data[i]);
}
infoLine();
 
/* Add padding codewords */
data[data_posn] = 0x00;
for (i = data_posn + 1; i < data_cw; i++) {
if ((i & 1) != 0) {
data[i] = 0x7e;
} else {
data[i] = 0x00;
}
}
 
/* Get block sizes */
n1 = GM_N1[layers - 1];
b1 = GM_B1[layers - 1];
n2 = n1 - 1;
b2 = GM_B2[layers - 1];
e1 = GM_EBEB[(layers - 1) * 20 + (ecc_level - 1) * 4];
b3 = GM_EBEB[(layers - 1) * 20 + (ecc_level - 1) * 4 + 1];
e2 = GM_EBEB[(layers - 1) * 20 + (ecc_level - 1) * 4 + 2];
 
/* Split the data into blocks */
wp = 0;
for (i = 0; i < b1 + b2; i++) {
if (i < b1) {
block_size = n1;
} else {
block_size = n2;
}
if (i < b3) {
ecc_size = e1;
} else {
ecc_size = e2;
}
data_size = block_size - ecc_size;
 
/* printf("block %d/%d: data %d / ecc %d\n", i + 1, (b1 + b2), data_size, ecc_size); */
for (j = 0; j < data_size; j++) {
data_block[j] = data[wp];
wp++;
}
 
/* Calculate ECC data for this block */
rs.init_gf(0x89);
rs.init_code(ecc_size, 1);
rs.encode(data_size, data_block);
for (j = 0; j < ecc_size; j++) {
ecc_block[j] = rs.getResult(j);
}
 
/* Correct error correction data but in reverse order */
for (j = 0; j < data_size; j++) {
block[j] = data_block[j];
}
for (j = 0; j < ecc_size; j++) {
block[j + data_size] = ecc_block[ecc_size - j - 1];
}
 
for (j = 0; j < n2; j++) {
this.word[(b1 + b2) * j + i] = block[j];
}
if (block_size == n1) {
this.word[(b1 + b2) * (n1 - 1) + i] = block[n1 - 1];
}
}
}
 
private void placeDataInGrid(final int modules, final int size) {
int x, y, macromodule, offset;
 
offset = 13 - (modules - 1) / 2;
for (y = 0; y < modules; y++) {
for (x = 0; x < modules; x++) {
macromodule = GM_MACRO_MATRIX[(y + offset) * 27 + x + offset];
placeMacroModule(x, y, this.word[macromodule * 2], this.word[macromodule * 2 + 1], size);
}
}
}
 
void placeMacroModule(final int x, final int y, final int word1, final int word2, final int size) {
int i, j;
 
i = x * 6 + 1;
j = y * 6 + 1;
 
if ((word2 & 0x40) != 0) {
this.grid[j * size + i + 2] = true;
}
if ((word2 & 0x20) != 0) {
this.grid[j * size + i + 3] = true;
}
if ((word2 & 0x10) != 0) {
this.grid[(j + 1) * size + i] = true;
}
if ((word2 & 0x08) != 0) {
this.grid[(j + 1) * size + i + 1] = true;
}
if ((word2 & 0x04) != 0) {
this.grid[(j + 1) * size + i + 2] = true;
}
if ((word2 & 0x02) != 0) {
this.grid[(j + 1) * size + i + 3] = true;
}
if ((word2 & 0x01) != 0) {
this.grid[(j + 2) * size + i] = true;
}
if ((word1 & 0x40) != 0) {
this.grid[(j + 2) * size + i + 1] = true;
}
if ((word1 & 0x20) != 0) {
this.grid[(j + 2) * size + i + 2] = true;
}
if ((word1 & 0x10) != 0) {
this.grid[(j + 2) * size + i + 3] = true;
}
if ((word1 & 0x08) != 0) {
this.grid[(j + 3) * size + i] = true;
}
if ((word1 & 0x04) != 0) {
this.grid[(j + 3) * size + i + 1] = true;
}
if ((word1 & 0x02) != 0) {
this.grid[(j + 3) * size + i + 2] = true;
}
if ((word1 & 0x01) != 0) {
this.grid[(j + 3) * size + i + 3] = true;
}
}
 
private void addLayerId(final int size, final int layers, final int modules, final int ecc_level) {
/* Place the layer ID into each macromodule */
 
int i, j, layer, start, stop;
final int[] layerid = new int[layers + 1];
final int[] id = new int[modules * modules];
 
/* Calculate Layer IDs */
for (i = 0; i <= layers; i++) {
if (ecc_level == 1) {
layerid[i] = 3 - i % 4;
} else {
layerid[i] = (i + 5 - ecc_level) % 4;
}
}
 
for (i = 0; i < modules; i++) {
for (j = 0; j < modules; j++) {
id[i * modules + j] = 0;
}
}
 
/* Calculate which value goes in each macromodule */
start = modules / 2;
stop = modules / 2;
for (layer = 0; layer <= layers; layer++) {
for (i = start; i <= stop; i++) {
id[start * modules + i] = layerid[layer];
id[i * modules + start] = layerid[layer];
id[(modules - start - 1) * modules + i] = layerid[layer];
id[i * modules + modules - start - 1] = layerid[layer];
}
start--;
stop++;
}
 
/* Place the data in the grid */
for (i = 0; i < modules; i++) {
for (j = 0; j < modules; j++) {
if ((id[i * modules + j] & 0x02) != 0) {
this.grid[(i * 6 + 1) * size + j * 6 + 1] = true;
}
if ((id[i * modules + j] & 0x01) != 0) {
this.grid[(i * 6 + 1) * size + j * 6 + 2] = true;
}
}
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code3Of9.java
New file
0,0 → 1,143
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
/**
* <p>
* Implements Code 39 bar code symbology according to ISO/IEC 16388:2007.
*
* <p>
* Input data can be of any length and supports the characters 0-9, A-Z, dash (-), full stop (.),
* space, asterisk (*), dollar ($), slash (/), plus (+) and percent (%). The standard does not
* require a check digit but a modulo-43 check digit can be added if required.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Code3Of9 extends Symbol {
 
public enum CheckDigit {
NONE, MOD43
}
 
private static final String[] CODE_39 = { "1112212111", "2112111121", "1122111121", "2122111111", "1112211121", "2112211111", "1122211111", "1112112121", "2112112111", "1122112111", "2111121121",
"1121121121", "2121121111", "1111221121", "2111221111", "1121221111", "1111122121", "2111122111", "1121122111", "1111222111", "2111111221", "1121111221", "2121111211", "1111211221",
"2111211211", "1121211211", "1111112221", "2111112211", "1121112211", "1111212211", "2211111121", "1221111121", "2221111111", "1211211121", "2211211111", "1221211111", "1211112121",
"2211112111", "1221112111", "1212121111", "1212111211", "1211121211", "1112121211" };
 
private static final char[] LOOKUP = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%' };
 
private CheckDigit checkOption = CheckDigit.NONE;
private double moduleWidthRatio = 2;
 
/**
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually between
* {@code 2} and {@code 3}. The default value is {@code 2}.
*
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width
*/
public void setModuleWidthRatio(final double moduleWidthRatio) {
this.moduleWidthRatio = moduleWidthRatio;
}
 
/**
* Returns the ratio of wide bar width to narrow bar width.
*
* @return the ratio of wide bar width to narrow bar width
*/
public double getModuleWidthRatio() {
return this.moduleWidthRatio;
}
 
/**
* Select addition of optional Modulo-43 check digit or encoding without check digit. By
* default, no check digit is added.
*
* @param checkMode Check digit option.
*/
public void setCheckDigit(final CheckDigit checkMode) {
this.checkOption = checkMode;
}
 
/**
* Returns the check digit mode.
*
* @return the check digit mode
*/
public CheckDigit getCheckDigit() {
return this.checkOption;
}
 
@Override
protected void encode() {
 
if (!this.content.matches("[0-9A-Z\\. \\-$/+%]*")) {
throw new OkapiException("Invalid characters in input");
}
 
final String start = "1211212111";
final String stop = "121121211";
 
final int patternLength = start.length() + stop.length() + 10 * this.content.length() + (this.checkOption == CheckDigit.MOD43 ? 10 : 0);
 
final StringBuilder dest = new StringBuilder(patternLength);
dest.append(start);
 
int counter = 0;
char checkDigit = ' ';
 
for (int i = 0; i < this.content.length(); i++) {
final char c = this.content.charAt(i);
final int index = positionOf(c, LOOKUP);
dest.append(CODE_39[index]);
counter += index;
}
 
if (this.checkOption == CheckDigit.MOD43) {
counter = counter % 43;
checkDigit = LOOKUP[counter];
final int index = positionOf(checkDigit, LOOKUP);
dest.append(CODE_39[index]);
if (checkDigit == ' ') {
// display a space check digit as _, otherwise it looks like an error
checkDigit = '_';
}
infoLine("Check Digit: " + checkDigit);
}
 
dest.append(stop);
 
if (this.checkOption == CheckDigit.MOD43) {
this.readable = "*" + this.content + checkDigit + "*";
} else {
this.readable = "*" + this.content + "*";
}
 
this.pattern = new String[] { dest.toString() };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
/** {@inheritDoc} */
@Override
protected double getModuleWidth(final int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return this.moduleWidthRatio;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/CodeOne.java
New file
0,0 → 1,1869
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
 
/**
* <p>
* Implements Code One.
*
* <p>
* Code One is able to encode the ISO 8859-1 (Latin-1) character set or GS1 data. There are two
* types of Code One symbol: variable height symbols which are roughly square (versions A thought to
* H) and fixed-height versions (version S and T). Version S symbols can only encode numeric data.
* The width of version S and version T symbols is determined by the length of the input data.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class CodeOne extends Symbol {
 
public enum Version {
NONE, A, B, C, D, E, F, G, H, S, T
}
 
private static final int[] C40_SHIFT = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 };
 
private static final int[] C40_VALUE = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22,
23, 24, 25, 26, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
 
private static final int[] TEXT_SHIFT = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3 };
 
private static final int[] TEXT_VALUE = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 22, 23, 24, 25,
26, 0, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31 };
 
private static final int[] C1_HEIGHT = { 16, 22, 28, 40, 52, 70, 104, 148 };
private static final int[] C1_WIDTH = { 18, 22, 32, 42, 54, 76, 98, 134 };
private static final int[] C1_DATA_LENGTH = { 10, 19, 44, 91, 182, 370, 732, 1480 };
private static final int[] C1_ECC_LENGTH = { 10, 16, 26, 44, 70, 140, 280, 560 };
private static final int[] C1_BLOCKS = { 1, 1, 1, 1, 1, 2, 4, 8 };
private static final int[] C1_DATA_BLOCKS = { 10, 19, 44, 91, 182, 185, 183, 185 };
private static final int[] C1_ECC_BLOCKS = { 10, 16, 26, 44, 70, 70, 70, 70 };
private static final int[] C1_GRID_WIDTH = { 4, 5, 7, 9, 12, 17, 22, 30 };
private static final int[] C1_GRID_HEIGHT = { 5, 7, 10, 15, 21, 30, 46, 68 };
 
private enum Mode {
C1_ASCII, C1_C40, C1_DECIMAL, C1_TEXT, C1_EDI, C1_BYTE
}
 
private Version preferredVersion = Version.NONE;
 
private final int[] data = new int[1500];
private final int[][] datagrid = new int[136][120];
private final boolean[][] outputGrid = new boolean[148][134];
 
/**
* Sets the preferred symbol size / version. Versions A to H are square symbols. Version S and T
* are fixed height symbols. This value may be ignored if the input data does not fit in the
* specified version.
*
* @param version the preferred symbol version
*/
public void setPreferredVersion(final Version version) {
this.preferredVersion = version;
}
 
/**
* Returns the preferred symbol version.
*
* @return the preferred symbol version
*/
public Version getPreferredVersion() {
return this.preferredVersion;
}
 
@Override
protected boolean gs1Supported() {
return true;
}
 
@Override
protected void encode() {
 
int size = 1, i, j, data_blocks;
int row, col;
int sub_version = 0;
int codewords;
final int[] ecc = new int[600];
final int[] stream = new int[2100];
int block_width;
final int length = this.content.length();
final ReedSolomon rs = new ReedSolomon();
int data_length;
int data_cw, ecc_cw;
final int[] sub_data = new int[190];
final StringBuilder bin = new StringBuilder();
 
if (!this.content.matches("[\u0000-\u00FF]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
if (this.preferredVersion == Version.S) {
/* Version S */
 
infoLine("Version: S");
 
if (length > 18) {
throw new OkapiException("Input data too long");
}
 
if (!this.content.matches("[0-9]+?")) {
throw new OkapiException("Invalid characters in input");
}
 
sub_version = 3;
codewords = 12;
block_width = 6; /* Version S-30 */
if (length <= 12) {
sub_version = 2;
codewords = 8;
block_width = 4;
} /* Version S-20 */
if (length <= 6) {
sub_version = 1;
codewords = 4;
block_width = 2;
} /* Version S-10 */
 
final BigInteger elreg = new BigInteger(this.content);
 
for (i = 0; i < codewords; i++) {
BigInteger codewordValue = elreg.shiftRight(5 * i);
codewordValue = codewordValue.and(BigInteger.valueOf(0b11111));
this.data[codewords - i - 1] = codewordValue.intValue();
}
 
logCodewords(codewords);
 
rs.init_gf(0x25);
rs.init_code(codewords, 1);
rs.encode(codewords, this.data);
 
infoLine("ECC Codeword Count: " + codewords);
 
for (i = 0; i < codewords; i++) {
stream[i] = this.data[i];
stream[i + codewords] = rs.getResult(codewords - i - 1);
}
 
for (i = 0; i < 136; i++) {
for (j = 0; j < 120; j++) {
this.datagrid[i][j] = '0';
}
}
 
i = 0;
for (row = 0; row < 2; row++) {
for (col = 0; col < block_width; col++) {
if ((stream[i] & 0x10) != 0) {
this.datagrid[row * 2][col * 5] = '1';
}
if ((stream[i] & 0x08) != 0) {
this.datagrid[row * 2][col * 5 + 1] = '1';
}
if ((stream[i] & 0x04) != 0) {
this.datagrid[row * 2][col * 5 + 2] = '1';
}
if ((stream[i] & 0x02) != 0) {
this.datagrid[row * 2 + 1][col * 5] = '1';
}
if ((stream[i] & 0x01) != 0) {
this.datagrid[row * 2 + 1][col * 5 + 1] = '1';
}
if ((stream[i + 1] & 0x10) != 0) {
this.datagrid[row * 2][col * 5 + 3] = '1';
}
if ((stream[i + 1] & 0x08) != 0) {
this.datagrid[row * 2][col * 5 + 4] = '1';
}
if ((stream[i + 1] & 0x04) != 0) {
this.datagrid[row * 2 + 1][col * 5 + 2] = '1';
}
if ((stream[i + 1] & 0x02) != 0) {
this.datagrid[row * 2 + 1][col * 5 + 3] = '1';
}
if ((stream[i + 1] & 0x01) != 0) {
this.datagrid[row * 2 + 1][col * 5 + 4] = '1';
}
i += 2;
}
}
 
infoLine("Grid Size: " + block_width + " X " + 2);
 
size = 9;
this.row_count = 8;
this.symbol_width = 10 * sub_version + 1;
}
 
if (this.preferredVersion == Version.T) {
/* Version T */
 
infoLine("Version: T");
 
for (i = 0; i < 40; i++) {
this.data[i] = 0;
}
data_length = encodeAsCode1Data();
 
if (data_length > 38) {
throw new OkapiException("Input data too long");
}
 
size = 10;
sub_version = 3;
data_cw = 38;
ecc_cw = 22;
block_width = 12;
if (data_length <= 24) {
sub_version = 2;
data_cw = 24;
ecc_cw = 16;
block_width = 8;
}
if (data_length <= 10) {
sub_version = 1;
data_cw = 10;
ecc_cw = 10;
block_width = 4;
}
 
logCodewords(data_length);
 
for (i = data_length; i < data_cw; i++) {
this.data[i] = 129; /* Pad */
}
 
/* Calculate error correction data */
rs.init_gf(0x12d);
rs.init_code(ecc_cw, 1);
rs.encode(data_cw, this.data);
 
infoLine("ECC Codeword Count: " + ecc_cw);
 
/* "Stream" combines data and error correction data */
for (i = 0; i < data_cw; i++) {
stream[i] = this.data[i];
}
for (i = 0; i < ecc_cw; i++) {
stream[data_cw + i] = rs.getResult(ecc_cw - i - 1);
}
 
for (i = 0; i < 136; i++) {
for (j = 0; j < 120; j++) {
this.datagrid[i][j] = '0';
}
}
 
i = 0;
for (row = 0; row < 5; row++) {
for (col = 0; col < block_width; col++) {
if ((stream[i] & 0x80) != 0) {
this.datagrid[row * 2][col * 4] = '1';
}
if ((stream[i] & 0x40) != 0) {
this.datagrid[row * 2][col * 4 + 1] = '1';
}
if ((stream[i] & 0x20) != 0) {
this.datagrid[row * 2][col * 4 + 2] = '1';
}
if ((stream[i] & 0x10) != 0) {
this.datagrid[row * 2][col * 4 + 3] = '1';
}
if ((stream[i] & 0x08) != 0) {
this.datagrid[row * 2 + 1][col * 4] = '1';
}
if ((stream[i] & 0x04) != 0) {
this.datagrid[row * 2 + 1][col * 4 + 1] = '1';
}
if ((stream[i] & 0x02) != 0) {
this.datagrid[row * 2 + 1][col * 4 + 2] = '1';
}
if ((stream[i] & 0x01) != 0) {
this.datagrid[row * 2 + 1][col * 4 + 3] = '1';
}
i++;
}
}
 
infoLine("Grid Size: " + block_width + " X " + 5);
 
this.row_count = 16;
this.symbol_width = sub_version * 16 + 1;
}
 
if (this.preferredVersion != Version.S && this.preferredVersion != Version.T) {
/* Version A to H */
for (i = 0; i < 1500; i++) {
this.data[i] = 0;
}
data_length = encodeAsCode1Data();
 
for (i = 7; i >= 0; i--) {
if (C1_DATA_LENGTH[i] >= data_length) {
size = i + 1;
}
}
 
if (getSize(this.preferredVersion) > size) {
size = getSize(this.preferredVersion);
}
 
final char version = (char) (size - 1 + 'A');
infoLine("Version: " + version);
logCodewords(data_length);
 
for (i = data_length; i < C1_DATA_LENGTH[size - 1]; i++) {
this.data[i] = 129; /* Pad */
}
 
/* Calculate error correction data */
data_length = C1_DATA_LENGTH[size - 1];
 
data_blocks = C1_BLOCKS[size - 1];
 
rs.init_gf(0x12d);
rs.init_code(C1_ECC_BLOCKS[size - 1], 0);
for (i = 0; i < data_blocks; i++) {
for (j = 0; j < C1_DATA_BLOCKS[size - 1]; j++) {
sub_data[j] = this.data[j * data_blocks + i];
}
rs.encode(C1_DATA_BLOCKS[size - 1], sub_data);
for (j = 0; j < C1_ECC_BLOCKS[size - 1]; j++) {
ecc[C1_ECC_LENGTH[size - 1] - (j * data_blocks + i) - 1] = rs.getResult(j);
}
}
 
infoLine("ECC Codeword Count: " + C1_ECC_LENGTH[size - 1]);
 
/* "Stream" combines data and error correction data */
for (i = 0; i < data_length; i++) {
stream[i] = this.data[i];
}
for (i = 0; i < C1_ECC_LENGTH[size - 1]; i++) {
stream[data_length + i] = ecc[i];
}
 
for (i = 0; i < 136; i++) {
for (j = 0; j < 120; j++) {
this.datagrid[i][j] = '0';
}
}
 
i = 0;
for (row = 0; row < C1_GRID_HEIGHT[size - 1]; row++) {
for (col = 0; col < C1_GRID_WIDTH[size - 1]; col++) {
if ((stream[i] & 0x80) != 0) {
this.datagrid[row * 2][col * 4] = '1';
}
if ((stream[i] & 0x40) != 0) {
this.datagrid[row * 2][col * 4 + 1] = '1';
}
if ((stream[i] & 0x20) != 0) {
this.datagrid[row * 2][col * 4 + 2] = '1';
}
if ((stream[i] & 0x10) != 0) {
this.datagrid[row * 2][col * 4 + 3] = '1';
}
if ((stream[i] & 0x08) != 0) {
this.datagrid[row * 2 + 1][col * 4] = '1';
}
if ((stream[i] & 0x04) != 0) {
this.datagrid[row * 2 + 1][col * 4 + 1] = '1';
}
if ((stream[i] & 0x02) != 0) {
this.datagrid[row * 2 + 1][col * 4 + 2] = '1';
}
if ((stream[i] & 0x01) != 0) {
this.datagrid[row * 2 + 1][col * 4 + 3] = '1';
}
i++;
}
}
 
infoLine("Grid Size: " + C1_GRID_WIDTH[size - 1] + " X " + C1_GRID_HEIGHT[size - 1]);
 
this.row_count = C1_HEIGHT[size - 1];
this.symbol_width = C1_WIDTH[size - 1];
}
 
for (i = 0; i < 148; i++) {
for (j = 0; j < 134; j++) {
this.outputGrid[i][j] = false;
}
}
 
switch (size) {
case 1:
/* Version A */
plotCentralFinder(6, 3, 1);
plotVerticalBar(4, 6, 1);
plotVerticalBar(12, 5, 0);
setGridModule(5, 12);
plotSpigot(0);
plotSpigot(15);
plotDataBlock(0, 0, 5, 4, 0, 0);
plotDataBlock(0, 4, 5, 12, 0, 2);
plotDataBlock(5, 0, 5, 12, 6, 0);
plotDataBlock(5, 12, 5, 4, 6, 2);
break;
case 2:
/* Version B */
plotCentralFinder(8, 4, 1);
plotVerticalBar(4, 8, 1);
plotVerticalBar(16, 7, 0);
setGridModule(7, 16);
plotSpigot(0);
plotSpigot(21);
plotDataBlock(0, 0, 7, 4, 0, 0);
plotDataBlock(0, 4, 7, 16, 0, 2);
plotDataBlock(7, 0, 7, 16, 8, 0);
plotDataBlock(7, 16, 7, 4, 8, 2);
break;
case 3:
/* Version C */
plotCentralFinder(11, 4, 2);
plotVerticalBar(4, 11, 1);
plotVerticalBar(26, 13, 1);
plotVerticalBar(4, 10, 0);
plotVerticalBar(26, 10, 0);
plotSpigot(0);
plotSpigot(27);
plotDataBlock(0, 0, 10, 4, 0, 0);
plotDataBlock(0, 4, 10, 20, 0, 2);
plotDataBlock(0, 24, 10, 4, 0, 4);
plotDataBlock(10, 0, 10, 4, 8, 0);
plotDataBlock(10, 4, 10, 20, 8, 2);
plotDataBlock(10, 24, 10, 4, 8, 4);
break;
case 4:
/* Version D */
plotCentralFinder(16, 5, 1);
plotVerticalBar(4, 16, 1);
plotVerticalBar(20, 16, 1);
plotVerticalBar(36, 16, 1);
plotVerticalBar(4, 15, 0);
plotVerticalBar(20, 15, 0);
plotVerticalBar(36, 15, 0);
plotSpigot(0);
plotSpigot(12);
plotSpigot(27);
plotSpigot(39);
plotDataBlock(0, 0, 15, 4, 0, 0);
plotDataBlock(0, 4, 15, 14, 0, 2);
plotDataBlock(0, 18, 15, 14, 0, 4);
plotDataBlock(0, 32, 15, 4, 0, 6);
plotDataBlock(15, 0, 15, 4, 10, 0);
plotDataBlock(15, 4, 15, 14, 10, 2);
plotDataBlock(15, 18, 15, 14, 10, 4);
plotDataBlock(15, 32, 15, 4, 10, 6);
break;
case 5:
/* Version E */
plotCentralFinder(22, 5, 2);
plotVerticalBar(4, 22, 1);
plotVerticalBar(26, 24, 1);
plotVerticalBar(48, 22, 1);
plotVerticalBar(4, 21, 0);
plotVerticalBar(26, 21, 0);
plotVerticalBar(48, 21, 0);
plotSpigot(0);
plotSpigot(12);
plotSpigot(39);
plotSpigot(51);
plotDataBlock(0, 0, 21, 4, 0, 0);
plotDataBlock(0, 4, 21, 20, 0, 2);
plotDataBlock(0, 24, 21, 20, 0, 4);
plotDataBlock(0, 44, 21, 4, 0, 6);
plotDataBlock(21, 0, 21, 4, 10, 0);
plotDataBlock(21, 4, 21, 20, 10, 2);
plotDataBlock(21, 24, 21, 20, 10, 4);
plotDataBlock(21, 44, 21, 4, 10, 6);
break;
case 6:
/* Version F */
plotCentralFinder(31, 5, 3);
plotVerticalBar(4, 31, 1);
plotVerticalBar(26, 35, 1);
plotVerticalBar(48, 31, 1);
plotVerticalBar(70, 35, 1);
plotVerticalBar(4, 30, 0);
plotVerticalBar(26, 30, 0);
plotVerticalBar(48, 30, 0);
plotVerticalBar(70, 30, 0);
plotSpigot(0);
plotSpigot(12);
plotSpigot(24);
plotSpigot(45);
plotSpigot(57);
plotSpigot(69);
plotDataBlock(0, 0, 30, 4, 0, 0);
plotDataBlock(0, 4, 30, 20, 0, 2);
plotDataBlock(0, 24, 30, 20, 0, 4);
plotDataBlock(0, 44, 30, 20, 0, 6);
plotDataBlock(0, 64, 30, 4, 0, 8);
plotDataBlock(30, 0, 30, 4, 10, 0);
plotDataBlock(30, 4, 30, 20, 10, 2);
plotDataBlock(30, 24, 30, 20, 10, 4);
plotDataBlock(30, 44, 30, 20, 10, 6);
plotDataBlock(30, 64, 30, 4, 10, 8);
break;
case 7:
/* Version G */
plotCentralFinder(47, 6, 2);
plotVerticalBar(6, 47, 1);
plotVerticalBar(27, 49, 1);
plotVerticalBar(48, 47, 1);
plotVerticalBar(69, 49, 1);
plotVerticalBar(90, 47, 1);
plotVerticalBar(6, 46, 0);
plotVerticalBar(27, 46, 0);
plotVerticalBar(48, 46, 0);
plotVerticalBar(69, 46, 0);
plotVerticalBar(90, 46, 0);
plotSpigot(0);
plotSpigot(12);
plotSpigot(24);
plotSpigot(36);
plotSpigot(67);
plotSpigot(79);
plotSpigot(91);
plotSpigot(103);
plotDataBlock(0, 0, 46, 6, 0, 0);
plotDataBlock(0, 6, 46, 19, 0, 2);
plotDataBlock(0, 25, 46, 19, 0, 4);
plotDataBlock(0, 44, 46, 19, 0, 6);
plotDataBlock(0, 63, 46, 19, 0, 8);
plotDataBlock(0, 82, 46, 6, 0, 10);
plotDataBlock(46, 0, 46, 6, 12, 0);
plotDataBlock(46, 6, 46, 19, 12, 2);
plotDataBlock(46, 25, 46, 19, 12, 4);
plotDataBlock(46, 44, 46, 19, 12, 6);
plotDataBlock(46, 63, 46, 19, 12, 8);
plotDataBlock(46, 82, 46, 6, 12, 10);
break;
case 8:
/* Version H */
plotCentralFinder(69, 6, 3);
plotVerticalBar(6, 69, 1);
plotVerticalBar(26, 73, 1);
plotVerticalBar(46, 69, 1);
plotVerticalBar(66, 73, 1);
plotVerticalBar(86, 69, 1);
plotVerticalBar(106, 73, 1);
plotVerticalBar(126, 69, 1);
plotVerticalBar(6, 68, 0);
plotVerticalBar(26, 68, 0);
plotVerticalBar(46, 68, 0);
plotVerticalBar(66, 68, 0);
plotVerticalBar(86, 68, 0);
plotVerticalBar(106, 68, 0);
plotVerticalBar(126, 68, 0);
plotSpigot(0);
plotSpigot(12);
plotSpigot(24);
plotSpigot(36);
plotSpigot(48);
plotSpigot(60);
plotSpigot(87);
plotSpigot(99);
plotSpigot(111);
plotSpigot(123);
plotSpigot(135);
plotSpigot(147);
plotDataBlock(0, 0, 68, 6, 0, 0);
plotDataBlock(0, 6, 68, 18, 0, 2);
plotDataBlock(0, 24, 68, 18, 0, 4);
plotDataBlock(0, 42, 68, 18, 0, 6);
plotDataBlock(0, 60, 68, 18, 0, 8);
plotDataBlock(0, 78, 68, 18, 0, 10);
plotDataBlock(0, 96, 68, 18, 0, 12);
plotDataBlock(0, 114, 68, 6, 0, 14);
plotDataBlock(68, 0, 68, 6, 12, 0);
plotDataBlock(68, 6, 68, 18, 12, 2);
plotDataBlock(68, 24, 68, 18, 12, 4);
plotDataBlock(68, 42, 68, 18, 12, 6);
plotDataBlock(68, 60, 68, 18, 12, 8);
plotDataBlock(68, 78, 68, 18, 12, 10);
plotDataBlock(68, 96, 68, 18, 12, 12);
plotDataBlock(68, 114, 68, 6, 12, 14);
break;
case 9:
/* Version S */
plotHorizontalBar(5, 1);
plotHorizontalBar(7, 1);
setGridModule(6, 0);
setGridModule(6, this.symbol_width - 1);
resetGridModule(7, 1);
resetGridModule(7, this.symbol_width - 2);
switch (sub_version) {
case 1:
/* Version S-10 */
setGridModule(0, 5);
plotDataBlock(0, 0, 4, 5, 0, 0);
plotDataBlock(0, 5, 4, 5, 0, 1);
break;
case 2:
/* Version S-20 */
setGridModule(0, 10);
setGridModule(4, 10);
plotDataBlock(0, 0, 4, 10, 0, 0);
plotDataBlock(0, 10, 4, 10, 0, 1);
break;
case 3:
/* Version S-30 */
setGridModule(0, 15);
setGridModule(4, 15);
setGridModule(6, 15);
plotDataBlock(0, 0, 4, 15, 0, 0);
plotDataBlock(0, 15, 4, 15, 0, 1);
break;
}
break;
case 10:
/* Version T */
plotHorizontalBar(11, 1);
plotHorizontalBar(13, 1);
plotHorizontalBar(15, 1);
setGridModule(12, 0);
setGridModule(12, this.symbol_width - 1);
setGridModule(14, 0);
setGridModule(14, this.symbol_width - 1);
resetGridModule(13, 1);
resetGridModule(13, this.symbol_width - 2);
resetGridModule(15, 1);
resetGridModule(15, this.symbol_width - 2);
switch (sub_version) {
case 1:
/* Version T-16 */
setGridModule(0, 8);
setGridModule(10, 8);
plotDataBlock(0, 0, 10, 8, 0, 0);
plotDataBlock(0, 8, 10, 8, 0, 1);
break;
case 2:
/* Version T-32 */
setGridModule(0, 16);
setGridModule(10, 16);
setGridModule(12, 16);
plotDataBlock(0, 0, 10, 16, 0, 0);
plotDataBlock(0, 16, 10, 16, 0, 1);
break;
case 3:
/* Verion T-48 */
setGridModule(0, 24);
setGridModule(10, 24);
setGridModule(12, 24);
setGridModule(14, 24);
plotDataBlock(0, 0, 10, 24, 0, 0);
plotDataBlock(0, 24, 10, 24, 0, 1);
break;
}
break;
}
 
this.readable = "";
this.pattern = new String[this.row_count];
this.row_height = new int[this.row_count];
for (i = 0; i < this.row_count; i++) {
bin.setLength(0);
for (j = 0; j < this.symbol_width; j++) {
if (this.outputGrid[i][j]) {
bin.append('1');
} else {
bin.append('0');
}
}
this.pattern[i] = bin2pat(bin);
this.row_height[i] = 1;
}
}
 
private void logCodewords(final int count) {
info("Codewords: ");
for (int i = 0; i < count; i++) {
infoSpace(this.data[i]);
}
infoLine();
}
 
private int encodeAsCode1Data() {
Mode current_mode, next_mode;
boolean latch;
boolean done;
int sourcePoint, targetPoint, i, j;
int c40_p;
int text_p;
int edi_p;
int byte_start = 0;
final int[] c40_buffer = new int[6];
final int[] text_buffer = new int[6];
final int[] edi_buffer = new int[6];
String decimal_binary = "";
int length;
int shift_set, value;
int data_left, decimal_count;
int sub_value;
int bits_left_in_byte, target_count;
boolean isTwoDigits;
 
this.inputData = toBytes(this.content, StandardCharsets.ISO_8859_1);
length = this.inputData.length;
 
sourcePoint = 0;
targetPoint = 0;
c40_p = 0;
text_p = 0;
edi_p = 0;
 
if (this.inputDataType == DataType.GS1) {
this.data[targetPoint] = 232;
targetPoint++;
} /* FNC1 */
 
/* Step A */
current_mode = Mode.C1_ASCII;
next_mode = Mode.C1_ASCII;
 
do {
if (current_mode != next_mode) {
/* Change mode */
switch (next_mode) {
case C1_C40:
this.data[targetPoint] = 230;
targetPoint++;
break;
case C1_TEXT:
this.data[targetPoint] = 239;
targetPoint++;
break;
case C1_EDI:
this.data[targetPoint] = 238;
targetPoint++;
break;
case C1_BYTE:
this.data[targetPoint] = 231;
targetPoint++;
break;
}
}
 
if (current_mode != Mode.C1_BYTE && next_mode == Mode.C1_BYTE) {
byte_start = targetPoint;
}
current_mode = next_mode;
 
if (current_mode == Mode.C1_ASCII) { /* Step B - ASCII encodation */
next_mode = Mode.C1_ASCII;
 
if (length - sourcePoint >= 21) { /* Step B1 */
j = 0;
 
for (i = 0; i < 21; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 21) {
next_mode = Mode.C1_DECIMAL;
decimal_binary += "1111";
}
}
 
if (next_mode == Mode.C1_ASCII && length - sourcePoint >= 13) { /* Step B2 */
j = 0;
 
for (i = 0; i < 13; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 13) {
latch = false;
for (i = sourcePoint + 13; i < length; i++) {
if (!(this.inputData[i] >= '0' && this.inputData[i] <= '9')) {
latch = true;
}
}
 
if (!latch) {
next_mode = Mode.C1_DECIMAL;
decimal_binary += "1111";
}
}
}
 
if (next_mode == Mode.C1_ASCII) { /* Step B3 */
isTwoDigits = false;
if (sourcePoint + 1 != length) {
if (this.inputData[sourcePoint] >= '0' && this.inputData[sourcePoint] <= '9') {
if (this.inputData[sourcePoint + 1] >= '0' && this.inputData[sourcePoint + 1] <= '9') {
// remaining data consists of two numeric digits
this.data[targetPoint] = 10 * (this.inputData[sourcePoint] - '0') + this.inputData[sourcePoint + 1] - '0' + 130;
targetPoint++;
sourcePoint += 2;
isTwoDigits = true;
}
}
}
 
if (!isTwoDigits) {
if (this.inputData[sourcePoint] == FNC1) {
if (length - sourcePoint >= 15) { /* Step B4 */
j = 0;
 
for (i = 0; i < 15; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 15) {
this.data[targetPoint] = 236; /* FNC1 and change to Decimal */
targetPoint++;
sourcePoint++;
next_mode = Mode.C1_DECIMAL;
}
}
 
if (length - sourcePoint >= 7) { /* Step B5 */
j = 0;
 
for (i = 0; i < 7; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 7) {
latch = false;
for (i = sourcePoint + 7; i < length; i++) {
if (!(this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9')) {
latch = true;
}
}
 
if (!latch) {
this.data[targetPoint] = 236; /*
* FNC1 and change to Decimal
*/
targetPoint++;
sourcePoint++;
next_mode = Mode.C1_DECIMAL;
}
}
}
}
 
if (next_mode == Mode.C1_ASCII) {
 
/* Step B6 */
next_mode = lookAheadTest(length, sourcePoint, current_mode);
 
if (next_mode == Mode.C1_ASCII) {
if (this.inputData[sourcePoint] > 127) {
/* Step B7 */
this.data[targetPoint] = 235;
targetPoint++; /* FNC4 */
this.data[targetPoint] = this.inputData[sourcePoint] - 128 + 1;
targetPoint++;
sourcePoint++;
} else {
/* Step B8 */
if (this.inputData[sourcePoint] == FNC1) {
this.data[targetPoint] = 232;
targetPoint++;
sourcePoint++; /* FNC1 */
} else {
this.data[targetPoint] = this.inputData[sourcePoint] + 1;
targetPoint++;
sourcePoint++;
}
}
}
}
}
}
}
 
if (current_mode == Mode.C1_C40) { /* Step C - C40 encodation */
done = false;
next_mode = Mode.C1_C40;
if (c40_p == 0) {
if (length - sourcePoint >= 12) {
j = 0;
 
for (i = 0; i < 12; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 12) {
next_mode = Mode.C1_ASCII;
done = true;
}
}
 
if (length - sourcePoint >= 8) {
j = 0;
 
for (i = 0; i < 8; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (length - sourcePoint == 8) {
latch = true;
} else {
latch = true;
for (j = sourcePoint + 8; j < length; j++) {
if (this.inputData[j] <= '0' || this.inputData[j] >= '9') {
latch = false;
}
}
}
 
if (j == 8 && latch) {
next_mode = Mode.C1_ASCII;
done = true;
}
}
 
if (!done) {
next_mode = lookAheadTest(length, sourcePoint, current_mode);
}
}
 
if (next_mode != Mode.C1_C40) {
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
} else {
if (this.inputData[sourcePoint] > 127) {
c40_buffer[c40_p] = 1;
c40_p++;
c40_buffer[c40_p] = 30;
c40_p++; /* Upper Shift */
shift_set = C40_SHIFT[this.inputData[sourcePoint] - 128];
value = C40_VALUE[this.inputData[sourcePoint] - 128];
} else {
shift_set = C40_SHIFT[this.inputData[sourcePoint]];
value = C40_VALUE[this.inputData[sourcePoint]];
}
 
if (this.inputData[sourcePoint] == FNC1) {
shift_set = 2;
value = 27; /* FNC1 */
}
 
if (shift_set != 0) {
c40_buffer[c40_p] = shift_set - 1;
c40_p++;
}
c40_buffer[c40_p] = value;
c40_p++;
 
if (c40_p >= 3) {
int iv;
 
iv = 1600 * c40_buffer[0] + 40 * c40_buffer[1] + c40_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
 
c40_buffer[0] = c40_buffer[3];
c40_buffer[1] = c40_buffer[4];
c40_buffer[2] = c40_buffer[5];
c40_buffer[3] = 0;
c40_buffer[4] = 0;
c40_buffer[5] = 0;
c40_p -= 3;
}
sourcePoint++;
}
}
 
if (current_mode == Mode.C1_TEXT) { /* Step D - Text encodation */
done = false;
next_mode = Mode.C1_TEXT;
if (text_p == 0) {
if (length - sourcePoint >= 12) {
j = 0;
 
for (i = 0; i < 12; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 12) {
next_mode = Mode.C1_ASCII;
done = true;
}
}
 
if (length - sourcePoint >= 8) {
j = 0;
 
for (i = 0; i < 8; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (length - sourcePoint == 8) {
latch = true;
} else {
latch = true;
for (j = sourcePoint + 8; j < length; j++) {
if (this.inputData[j] <= '0' || this.inputData[j] >= '9') {
latch = false;
}
}
}
 
if (j == 8 && latch) {
next_mode = Mode.C1_ASCII;
done = true;
}
}
 
if (!done) {
next_mode = lookAheadTest(length, sourcePoint, current_mode);
}
}
 
if (next_mode != Mode.C1_TEXT) {
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
} else {
if (this.inputData[sourcePoint] > 127) {
text_buffer[text_p] = 1;
text_p++;
text_buffer[text_p] = 30;
text_p++; /* Upper Shift */
shift_set = TEXT_SHIFT[this.inputData[sourcePoint] - 128];
value = TEXT_VALUE[this.inputData[sourcePoint] - 128];
} else {
shift_set = TEXT_SHIFT[this.inputData[sourcePoint]];
value = TEXT_VALUE[this.inputData[sourcePoint]];
}
 
if (this.inputData[sourcePoint] == FNC1) {
shift_set = 2;
value = 27; /* FNC1 */
}
 
if (shift_set != 0) {
text_buffer[text_p] = shift_set - 1;
text_p++;
}
text_buffer[text_p] = value;
text_p++;
 
if (text_p >= 3) {
int iv;
 
iv = 1600 * text_buffer[0] + 40 * text_buffer[1] + text_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
 
text_buffer[0] = text_buffer[3];
text_buffer[1] = text_buffer[4];
text_buffer[2] = text_buffer[5];
text_buffer[3] = 0;
text_buffer[4] = 0;
text_buffer[5] = 0;
text_p -= 3;
}
sourcePoint++;
}
}
 
if (current_mode == Mode.C1_EDI) { /* Step E - EDI Encodation */
 
value = 0;
next_mode = Mode.C1_EDI;
if (edi_p == 0) {
if (length - sourcePoint >= 12) {
j = 0;
 
for (i = 0; i < 12; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (j == 12) {
next_mode = Mode.C1_ASCII;
}
}
 
if (length - sourcePoint >= 8) {
j = 0;
 
for (i = 0; i < 8; i++) {
if (this.inputData[sourcePoint + i] >= '0' && this.inputData[sourcePoint + i] <= '9') {
j++;
}
}
 
if (length - sourcePoint == 8) {
latch = true;
} else {
latch = true;
for (j = sourcePoint + 8; j < length; j++) {
if (this.inputData[j] <= '0' || this.inputData[j] >= '9') {
latch = false;
}
}
}
 
if (j == 8 && latch) {
next_mode = Mode.C1_ASCII;
}
}
 
if (!(isEdiEncodable(this.inputData[sourcePoint]) && isEdiEncodable(this.inputData[sourcePoint + 1]) && isEdiEncodable(this.inputData[sourcePoint + 2]))) {
next_mode = Mode.C1_ASCII;
}
}
 
if (next_mode != Mode.C1_EDI) {
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
} else {
if (this.inputData[sourcePoint] == 13) {
value = 0;
}
if (this.inputData[sourcePoint] == '*') {
value = 1;
}
if (this.inputData[sourcePoint] == '>') {
value = 2;
}
if (this.inputData[sourcePoint] == ' ') {
value = 3;
}
if (this.inputData[sourcePoint] >= '0' && this.inputData[sourcePoint] <= '9') {
value = this.inputData[sourcePoint] - '0' + 4;
}
if (this.inputData[sourcePoint] >= 'A' && this.inputData[sourcePoint] <= 'Z') {
value = this.inputData[sourcePoint] - 'A' + 14;
}
 
edi_buffer[edi_p] = value;
edi_p++;
 
if (edi_p >= 3) {
int iv;
 
iv = 1600 * edi_buffer[0] + 40 * edi_buffer[1] + edi_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
 
edi_buffer[0] = edi_buffer[3];
edi_buffer[1] = edi_buffer[4];
edi_buffer[2] = edi_buffer[5];
edi_buffer[3] = 0;
edi_buffer[4] = 0;
edi_buffer[5] = 0;
edi_p -= 3;
}
sourcePoint++;
}
}
 
if (current_mode == Mode.C1_DECIMAL) { /* Step F - Decimal encodation */
 
next_mode = Mode.C1_DECIMAL;
 
data_left = length - sourcePoint;
decimal_count = 0;
 
if (data_left >= 1) {
if (this.inputData[sourcePoint] >= '0' && this.inputData[sourcePoint] <= '9') {
decimal_count = 1;
}
}
if (data_left >= 2) {
if (decimal_count == 1 && this.inputData[sourcePoint + 1] >= '0' && this.inputData[sourcePoint + 1] <= '9') {
decimal_count = 2;
}
}
if (data_left >= 3) {
if (decimal_count == 2 && this.inputData[sourcePoint + 2] >= '0' && this.inputData[sourcePoint + 2] <= '9') {
decimal_count = 3;
}
}
 
if (decimal_count != 3) {
 
/* Finish Decimal mode and go back to ASCII */
 
decimal_binary += "111111"; /* Unlatch */
 
target_count = 3;
if (decimal_binary.length() <= 16) {
target_count = 2;
}
if (decimal_binary.length() <= 8) {
target_count = 1;
}
bits_left_in_byte = 8 * target_count - decimal_binary.length();
if (bits_left_in_byte == 8) {
bits_left_in_byte = 0;
}
 
if (bits_left_in_byte == 2) {
decimal_binary += "01";
}
 
if (bits_left_in_byte == 4 || bits_left_in_byte == 6) {
if (decimal_count >= 1) {
sub_value = this.inputData[sourcePoint] - '0' + 1;
 
for (i = 0x08; i > 0; i = i >> 1) {
if ((sub_value & i) != 0) {
decimal_binary += "1";
} else {
decimal_binary += "0";
}
}
sourcePoint++;
} else {
decimal_binary += "1111";
}
}
 
if (bits_left_in_byte == 6) {
decimal_binary += "01";
}
 
/* Binary buffer is full - transfer to data */
if (target_count >= 1) {
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(i) == '1') {
this.data[targetPoint] += 128 >> i;
}
}
targetPoint++;
}
if (target_count >= 2) {
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(8 + i) == '1') {
this.data[targetPoint] += 128 >> i;
}
 
}
targetPoint++;
}
if (target_count == 3) {
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(16 + i) == '1') {
this.data[targetPoint] += 128 >> i;
}
 
}
targetPoint++;
}
 
next_mode = Mode.C1_ASCII;
} else {
/* There are three digits - convert the value to binary */
value = 100 * (this.inputData[sourcePoint] - '0') + 10 * (this.inputData[sourcePoint + 1] - '0') + this.inputData[sourcePoint + 2] - '0' + 1;
 
for (i = 0x200; i > 0; i = i >> 1) {
if ((value & i) != 0) {
decimal_binary += "1";
} else {
decimal_binary += "0";
}
}
sourcePoint += 3;
}
 
if (decimal_binary.length() >= 24) {
/* Binary buffer is full - transfer to data */
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(i) == '1') {
this.data[targetPoint] += 128 >> i;
}
 
if (decimal_binary.charAt(8 + i) == '1') {
this.data[targetPoint + 1] += 128 >> i;
}
 
if (decimal_binary.charAt(16 + i) == '1') {
this.data[targetPoint + 2] += 128 >> i;
}
 
}
targetPoint += 3;
 
if (decimal_binary.length() > 24) {
decimal_binary = decimal_binary.substring(24);
}
}
}
 
if (current_mode == Mode.C1_BYTE) {
next_mode = Mode.C1_BYTE;
 
if (this.inputData[sourcePoint] == FNC1) {
next_mode = Mode.C1_ASCII;
} else {
if (this.inputData[sourcePoint] <= 127) {
next_mode = lookAheadTest(length, sourcePoint, current_mode);
}
}
 
if (next_mode != Mode.C1_BYTE) {
/* Insert byte field length */
if (targetPoint - byte_start <= 249) {
for (i = targetPoint; i >= byte_start; i--) {
this.data[i + 1] = this.data[i];
}
this.data[byte_start] = targetPoint - byte_start;
targetPoint++;
} else {
for (i = targetPoint; i >= byte_start; i--) {
this.data[i + 2] = this.data[i];
}
this.data[byte_start] = 249 + (targetPoint - byte_start) / 250;
this.data[byte_start + 1] = (targetPoint - byte_start) % 250;
targetPoint += 2;
}
} else {
this.data[targetPoint] = this.inputData[sourcePoint];
targetPoint++;
sourcePoint++;
}
}
 
if (targetPoint > 1480) {
/* Data is too large for symbol */
throw new OkapiException("Input data too long");
}
 
} while (sourcePoint < length);
 
/* Empty buffers */
if (c40_p == 2) {
int iv;
 
c40_buffer[2] = 1;
iv = 1600 * c40_buffer[0] + 40 * c40_buffer[1] + c40_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
}
if (c40_p == 1) {
int iv;
 
c40_buffer[1] = 1;
c40_buffer[2] = 31; /* Pad */
iv = 1600 * c40_buffer[0] + 40 * c40_buffer[1] + c40_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
}
if (text_p == 2) {
int iv;
 
text_buffer[2] = 1;
iv = 1600 * text_buffer[0] + 40 * text_buffer[1] + text_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
}
if (text_p == 1) {
int iv;
 
text_buffer[1] = 1;
text_buffer[2] = 31; /* Pad */
iv = 1600 * text_buffer[0] + 40 * text_buffer[1] + text_buffer[2] + 1;
this.data[targetPoint] = iv / 256;
targetPoint++;
this.data[targetPoint] = iv % 256;
targetPoint++;
this.data[targetPoint] = 255;
targetPoint++; /* Unlatch */
}
 
if (current_mode == Mode.C1_DECIMAL) {
/* Finish Decimal mode and go back to ASCII */
 
decimal_binary += "111111"; /* Unlatch */
 
target_count = 3;
if (decimal_binary.length() <= 16) {
target_count = 2;
}
if (decimal_binary.length() <= 8) {
target_count = 1;
}
bits_left_in_byte = 8 * target_count - decimal_binary.length();
if (bits_left_in_byte == 8) {
bits_left_in_byte = 0;
}
 
if (bits_left_in_byte == 2) {
decimal_binary += "01";
}
 
if (bits_left_in_byte == 4 || bits_left_in_byte == 6) {
decimal_binary += "1111";
}
 
if (bits_left_in_byte == 6) {
decimal_binary += "01";
}
 
/* Binary buffer is full - transfer to data */
if (target_count >= 1) {
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(i) == '1') {
this.data[targetPoint] += 128 >> i;
}
}
targetPoint++;
}
if (target_count >= 2) {
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(8 + i) == '1') {
this.data[targetPoint] += 128 >> i;
}
}
targetPoint++;
}
if (target_count == 3) {
for (i = 0; i < 8; i++) {
if (decimal_binary.charAt(16 + i) == '1') {
this.data[targetPoint] += 128 >> i;
}
}
targetPoint++;
}
}
 
if (current_mode == Mode.C1_BYTE) {
/* Insert byte field length */
if (targetPoint - byte_start <= 249) {
for (i = targetPoint; i >= byte_start; i--) {
this.data[i + 1] = this.data[i];
}
this.data[byte_start] = targetPoint - byte_start;
targetPoint++;
} else {
for (i = targetPoint; i >= byte_start; i--) {
this.data[i + 2] = this.data[i];
}
this.data[byte_start] = 249 + (targetPoint - byte_start) / 250;
this.data[byte_start + 1] = (targetPoint - byte_start) % 250;
targetPoint += 2;
}
}
 
/* Re-check length of data */
if (targetPoint > 1480) {
/* Data is too large for symbol */
throw new OkapiException("Input data too long");
}
 
return targetPoint;
}
 
private Mode lookAheadTest(final int sourcelen, final int position, final Mode current_mode) {
double ascii_count, c40_count, text_count, edi_count, byte_count;
int reduced_char;
int done, best_count, sp;
Mode best_scheme;
 
/* Step J */
if (current_mode == Mode.C1_ASCII) {
ascii_count = 0.0;
c40_count = 1.0;
text_count = 1.0;
edi_count = 1.0;
byte_count = 2.0;
} else {
ascii_count = 1.0;
c40_count = 2.0;
text_count = 2.0;
edi_count = 2.0;
byte_count = 3.0;
}
 
switch (current_mode) {
case C1_C40:
c40_count = 0.0;
break;
case C1_TEXT:
text_count = 0.0;
break;
case C1_BYTE:
byte_count = 0.0;
break;
case C1_EDI:
edi_count = 0.0;
break;
}
 
for (sp = position; sp < sourcelen && sp <= position + 8; sp++) {
 
if (this.inputData[sp] <= 127) {
reduced_char = this.inputData[sp];
} else {
reduced_char = this.inputData[sp] - 127;
}
 
/* Step L */
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') {
ascii_count += 0.5;
} else {
ascii_count = roundUpToNextInteger(ascii_count);
if (this.inputData[sp] > 127) {
ascii_count += 2.0;
} else {
ascii_count += 1.0;
}
}
 
/* Step M */
done = 0;
if (reduced_char == ' ') {
c40_count += 2.0 / 3.0;
done = 1;
}
if (reduced_char >= '0' && reduced_char <= '9') {
c40_count += 2.0 / 3.0;
done = 1;
}
if (reduced_char >= 'A' && reduced_char <= 'Z') {
c40_count += 2.0 / 3.0;
done = 1;
}
if (this.inputData[sp] > 127) {
c40_count += 4.0 / 3.0;
}
if (done == 0) {
c40_count += 4.0 / 3.0;
}
 
/* Step N */
done = 0;
if (reduced_char == ' ') {
text_count += 2.0 / 3.0;
done = 1;
}
if (reduced_char >= '0' && reduced_char <= '9') {
text_count += 2.0 / 3.0;
done = 1;
}
if (reduced_char >= 'a' && reduced_char <= 'z') {
text_count += 2.0 / 3.0;
done = 1;
}
if (this.inputData[sp] > 127) {
text_count += 4.0 / 3.0;
}
if (done == 0) {
text_count += 4.0 / 3.0;
}
 
/* Step O */
done = 0;
if (this.inputData[sp] == 13) {
edi_count += 2.0 / 3.0;
done = 1;
}
if (this.inputData[sp] == '*') {
edi_count += 2.0 / 3.0;
done = 1;
}
if (this.inputData[sp] == '>') {
edi_count += 2.0 / 3.0;
done = 1;
}
if (this.inputData[sp] == ' ') {
edi_count += 2.0 / 3.0;
done = 1;
}
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') {
edi_count += 2.0 / 3.0;
done = 1;
}
if (this.inputData[sp] >= 'A' && this.inputData[sp] <= 'Z') {
edi_count += 2.0 / 3.0;
done = 1;
}
if (this.inputData[sp] > 127) {
edi_count += 13.0 / 3.0;
} else {
if (done == 0) {
edi_count += 10.0 / 3.0;
}
}
 
/* Step P */
if (this.inputData[sp] == FNC1) {
byte_count += 3.0;
} else {
byte_count += 1.0;
}
 
}
 
ascii_count = roundUpToNextInteger(ascii_count);
c40_count = roundUpToNextInteger(c40_count);
text_count = roundUpToNextInteger(text_count);
edi_count = roundUpToNextInteger(edi_count);
byte_count = roundUpToNextInteger(byte_count);
best_scheme = Mode.C1_ASCII;
 
if (sp == sourcelen) {
/* Step K */
best_count = (int) edi_count;
 
if (text_count <= best_count) {
best_count = (int) text_count;
best_scheme = Mode.C1_TEXT;
}
 
if (c40_count <= best_count) {
best_count = (int) c40_count;
best_scheme = Mode.C1_C40;
}
 
if (ascii_count <= best_count) {
best_count = (int) ascii_count;
best_scheme = Mode.C1_ASCII;
}
 
if (byte_count <= best_count) {
best_scheme = Mode.C1_BYTE;
}
} else {
/* Step Q */
 
if (edi_count + 1.0 <= ascii_count && edi_count + 1.0 <= c40_count && edi_count + 1.0 <= byte_count && edi_count + 1.0 <= text_count) {
best_scheme = Mode.C1_EDI;
}
 
if (c40_count + 1.0 <= ascii_count && c40_count + 1.0 <= text_count) {
 
if (c40_count < edi_count) {
best_scheme = Mode.C1_C40;
} else {
if (c40_count == edi_count) {
if (preferEdi(sourcelen, position)) {
best_scheme = Mode.C1_EDI;
} else {
best_scheme = Mode.C1_C40;
}
}
}
}
 
if (text_count + 1.0 <= ascii_count && text_count + 1.0 <= c40_count && text_count + 1.0 <= byte_count && text_count + 1.0 <= edi_count) {
best_scheme = Mode.C1_TEXT;
}
 
if (ascii_count + 1.0 <= byte_count && ascii_count + 1.0 <= c40_count && ascii_count + 1.0 <= text_count && ascii_count + 1.0 <= edi_count) {
best_scheme = Mode.C1_ASCII;
}
 
if (byte_count + 1.0 <= ascii_count && byte_count + 1.0 <= c40_count && byte_count + 1.0 <= text_count && byte_count + 1.0 <= edi_count) {
best_scheme = Mode.C1_BYTE;
}
}
 
return best_scheme;
}
 
private double roundUpToNextInteger(final double input) {
double fraction, output;
 
fraction = input - (int) input;
if (fraction > 0.01) {
output = input - fraction + 1.0;
} else {
output = input;
}
 
return output;
}
 
private boolean preferEdi(final int sourcelen, final int position) {
int i;
 
for (i = position; isEdiEncodable(this.inputData[position + i]) && position + i < sourcelen; i++) {
;
}
 
if (position + i == sourcelen) {
/* Reached end of input */
return false;
}
 
if (this.inputData[position + i - 1] == 13) {
return true;
}
if (this.inputData[position + i - 1] == '*') {
return true;
}
if (this.inputData[position + i - 1] == '>') {
return true;
}
 
return false;
}
 
private boolean isEdiEncodable(final int input) {
boolean result = false;
 
if (input == 13) {
result = true;
}
if (input == '*') {
result = true;
}
if (input == '>') {
result = true;
}
if (input == ' ') {
result = true;
}
if (input >= '0' && input <= '9') {
result = true;
}
if (input >= 'A' && input <= 'Z') {
result = true;
}
 
return result;
}
 
private void plotCentralFinder(final int start_row, final int row_count, final int full_rows) {
for (int i = 0; i < row_count; i++) {
if (i < full_rows) {
plotHorizontalBar(start_row + i * 2, 1);
} else {
plotHorizontalBar(start_row + i * 2, 0);
if (i != row_count - 1) {
setGridModule(start_row + i * 2 + 1, 1);
setGridModule(start_row + i * 2 + 1, this.symbol_width - 2);
}
}
}
}
 
private void plotHorizontalBar(final int row_no, final int full) {
if (full != 0) {
for (int i = 0; i < this.symbol_width; i++) {
setGridModule(row_no, i);
}
} else {
for (int i = 1; i < this.symbol_width - 1; i++) {
setGridModule(row_no, i);
}
}
}
 
private void plotVerticalBar(final int column, final int height, final int top) {
if (top != 0) {
for (int i = 0; i < height; i++) {
setGridModule(i, column);
}
} else {
for (int i = 0; i < height; i++) {
setGridModule(this.row_count - i - 1, column);
}
}
}
 
private void plotSpigot(final int row_no) {
for (int i = this.symbol_width - 1; i > 0; i--) {
if (this.outputGrid[row_no][i - 1]) {
setGridModule(row_no, i);
}
}
}
 
private void plotDataBlock(final int start_row, final int start_col, final int height, final int width, final int row_offset, final int col_offset) {
for (int i = start_row; i < start_row + height; i++) {
for (int j = start_col; j < start_col + width; j++) {
if (this.datagrid[i][j] == '1') {
setGridModule(i + row_offset, j + col_offset);
}
}
}
}
 
private void setGridModule(final int row, final int column) {
this.outputGrid[row][column] = true;
}
 
private void resetGridModule(final int row, final int column) {
this.outputGrid[row][column] = false;
}
 
private static int getSize(final Version version) {
switch (version) {
case A:
return 1;
case B:
return 2;
case C:
return 3;
case D:
return 4;
case E:
return 5;
case F:
return 6;
case G:
return 7;
case H:
return 8;
default:
return 0;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code32.java
New file
0,0 → 1,107
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
/**
* <p>
* Implements Code 32, also known as Italian Pharmacode, A variation of Code 39 used by the Italian
* Ministry of Health ("Ministero della Sanità")
*
* <p>
* Requires a numeric input up to 8 digits in length. Check digit is calculated.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Code32 extends Symbol {
 
private static final char[] TABLE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
'Z' };
 
@Override
protected void encode() {
int i, checksum, checkpart, checkdigit;
int pharmacode, remainder, devisor;
String localstr, risultante;
final int[] codeword = new int[6];
final Code3Of9 c39 = new Code3Of9();
 
if (this.content.length() > 8) {
throw new OkapiException("Input too long");
}
 
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in input");
}
 
/* Add leading zeros as required */
localstr = "";
for (i = this.content.length(); i < 8; i++) {
localstr += "0";
}
localstr += this.content;
 
/* Calculate the check digit */
checksum = 0;
checkpart = 0;
for (i = 0; i < 4; i++) {
checkpart = Character.getNumericValue(localstr.charAt(i * 2));
checksum += checkpart;
checkpart = 2 * Character.getNumericValue(localstr.charAt(i * 2 + 1));
if (checkpart >= 10) {
checksum += checkpart - 10 + 1;
} else {
checksum += checkpart;
}
}
 
/* Add check digit to data string */
checkdigit = checksum % 10;
final char check = (char) (checkdigit + '0');
localstr += check;
infoLine("Check Digit: " + check);
 
/* Convert string into an integer value */
pharmacode = 0;
for (i = 0; i < localstr.length(); i++) {
pharmacode *= 10;
pharmacode += Character.getNumericValue(localstr.charAt(i));
}
 
/* Convert from decimal to base-32 */
devisor = 33554432;
for (i = 5; i >= 0; i--) {
codeword[i] = pharmacode / devisor;
remainder = pharmacode % devisor;
pharmacode = remainder;
devisor /= 32;
}
 
/* Look up values in 'Tabella di conversione' */
risultante = "";
for (i = 5; i >= 0; i--) {
risultante += TABLE[codeword[i]];
}
 
/* Plot the barcode using Code 39 */
 
this.readable = "A" + localstr;
this.pattern = new String[1];
this.row_count = 1;
this.row_height = new int[] { -1 };
infoLine("Code 39 Equivalent: " + risultante);
 
c39.setContent(risultante);
this.pattern[0] = c39.pattern[0];
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/HumanReadableLocation.java
New file
0,0 → 1,30
/*
* Copyright 2015 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
/**
* The location of a bar code's human-readable text.
*/
public enum HumanReadableLocation {
 
/** Display the human-readable text below the bar code. */
BOTTOM,
 
/** Display the human-readable text above the bar code. */
TOP,
 
/** Do not display the human-readable text. */
NONE
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/EanUpcAddOn.java
New file
0,0 → 1,112
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
/**
* <p>
* Implements EAN/UPC add-on bar code symbology according to BS EN 797:1996.
*
* @see Ean
* @see Upc
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class EanUpcAddOn extends Symbol {
 
private static final String[] EAN_SET_A = { "3211", "2221", "2122", "1411", "1132", "1231", "1114", "1312", "1213", "3112" };
 
private static final String[] EAN_SET_B = { "1123", "1222", "2212", "1141", "2311", "1321", "4111", "2131", "3121", "2113" };
 
private static final String[] EAN2_PARITY = { "AA", "AB", "BA", "BB" };
 
private static final String[] EAN5_PARITY = { "BBAAA", "BABAA", "BAABA", "BAAAB", "ABBAA", "AABBA", "AAABB", "ABABA", "ABAAB", "AABAB" };
 
@Override
protected void encode() {
 
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in input");
}
 
if (this.content.length() > 5) {
throw new OkapiException("Input data too long");
}
 
final int targetLength = this.content.length() > 2 ? 5 : 2;
 
if (this.content.length() < targetLength) {
for (int i = this.content.length(); i < targetLength; i++) {
this.content = '0' + this.content;
}
}
 
final String bars = targetLength == 2 ? ean2(this.content) : ean5(this.content);
 
this.readable = this.content;
this.pattern = new String[] { bars };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
private static String ean2(final String content) {
 
final int sum = (content.charAt(0) - '0') * 10 + content.charAt(1) - '0';
final String parity = EAN2_PARITY[sum % 4];
 
final StringBuilder sb = new StringBuilder();
sb.append("112"); /* Start */
for (int i = 0; i < 2; i++) {
final int val = content.charAt(i) - '0';
if (parity.charAt(i) == 'B') {
sb.append(EAN_SET_B[val]);
} else {
sb.append(EAN_SET_A[val]);
}
if (i != 1) { /* Glyph separator */
sb.append("11");
}
}
 
return sb.toString();
}
 
private static String ean5(final String content) {
 
int sum = 0;
for (int i = 0; i < 5; i++) {
if (i % 2 == 0) {
sum += 3 * (content.charAt(i) - '0');
} else {
sum += 9 * (content.charAt(i) - '0');
}
}
final String parity = EAN5_PARITY[sum % 10];
 
final StringBuilder sb = new StringBuilder();
sb.append("112"); /* Start */
for (int i = 0; i < 5; i++) {
final int val = content.charAt(i) - '0';
if (parity.charAt(i) == 'B') {
sb.append(EAN_SET_B[val]);
} else {
sb.append(EAN_SET_A[val]);
}
if (i != 4) { /* Glyph separator */
sb.append("11");
}
}
 
return sb.toString();
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/OkapiException.java
New file
0,0 → 1,36
/*
* Copyright 2015 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
/**
* An Okapi Barcode exception.
*
* @author Daniel Gredler
*/
public class OkapiException extends RuntimeException {
 
/** Serial version UID. */
private static final long serialVersionUID = -630504124631140642L;
 
/**
* Creates a new instance.
*
* @param message the error message
*/
public OkapiException(final String message) {
super(message);
}
 
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/KixCode.java
New file
0,0 → 1,107
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
import java.awt.geom.Rectangle2D;
import java.util.Locale;
 
/**
* <p>
* Implements Dutch Post KIX Code as used by Royal Dutch TPG Post (Netherlands).
*
* <p>
* The input data can consist of digits 0-9 and characters A-Z, and should be 11 characters in
* length. No check digit is added.
*
* <p>
* KIX Code is the same as RM4SCC, but without the check digit.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @see <a href="http://www.tntpost.nl/zakelijk/klantenservice/downloads/kIX_code/download.aspx">KIX
* Code Specification</a>
*/
public class KixCode extends Symbol {
 
private static final String[] ROYAL_TABLE = { "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT",
"ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", "FTAD", "FTFT", "FDAT", "AADD", "AFTD", "AFDT", "FATD", "FADT", "FFTT" };
 
private static final char[] KR_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z' };
 
@Override
protected void encode() {
 
this.content = this.content.toUpperCase(Locale.ENGLISH);
 
if (!this.content.matches("[0-9A-Z]+")) {
throw new OkapiException("Invalid characters in data");
}
 
final StringBuilder sb = new StringBuilder(this.content.length());
for (int i = 0; i < this.content.length(); i++) {
final int j = positionOf(this.content.charAt(i), KR_SET);
sb.append(ROYAL_TABLE[j]);
}
 
final String dest = sb.toString();
infoLine("Encoding: " + dest);
 
this.readable = "";
this.pattern = new String[] { dest };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
 
this.rectangles.clear();
x = 0;
w = 1;
y = 0;
h = 0;
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
final char c = this.pattern[0].charAt(xBlock);
switch (c) {
case 'A':
y = 0;
h = 5;
break;
case 'D':
y = 3;
h = 5;
break;
case 'F':
y = 0;
h = 8;
break;
case 'T':
y = 3;
h = 2;
break;
default:
throw new IllegalStateException("Unknown pattern character: " + c);
}
this.rectangles.add(new Rectangle2D.Double(x, y, w, h));
x += 2;
}
this.symbol_width = (this.pattern[0].length() - 1) * 2 + 1; // final bar doesn't need extra
// whitespace
this.symbol_height = 8;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/ReedSolomon.java
New file
0,0 → 1,103
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
/**
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class ReedSolomon {
private int logmod;
private int rlen;
 
private int[] logt;
private int[] alog;
private int[] rspoly;
public int[] res;
 
public int getResult(final int count) {
return this.res[count];
}
 
public void init_gf(final int poly) {
int m, b, p, v;
 
// Find the top bit, and hence the symbol size
for (b = 1, m = 0; b <= poly; b <<= 1) {
m++;
}
b >>= 1;
m--;
 
// Calculate the log/alog tables
this.logmod = (1 << m) - 1;
this.logt = new int[this.logmod + 1];
this.alog = new int[this.logmod];
 
for (p = 1, v = 0; v < this.logmod; v++) {
this.alog[v] = p;
this.logt[p] = v;
p <<= 1;
if ((p & b) != 0) {
p ^= poly;
}
}
}
 
public void init_code(final int nsym, int index) {
int i, k;
 
this.rspoly = new int[nsym + 1];
 
this.rlen = nsym;
 
this.rspoly[0] = 1;
for (i = 1; i <= nsym; i++) {
this.rspoly[i] = 1;
for (k = i - 1; k > 0; k--) {
if (this.rspoly[k] != 0) {
this.rspoly[k] = this.alog[(this.logt[this.rspoly[k]] + index) % this.logmod];
}
this.rspoly[k] ^= this.rspoly[k - 1];
}
this.rspoly[0] = this.alog[(this.logt[this.rspoly[0]] + index) % this.logmod];
index++;
}
}
 
public void encode(final int len, final int[] data) {
int i, k, m;
 
this.res = new int[this.rlen];
for (i = 0; i < this.rlen; i++) {
this.res[i] = 0;
}
 
for (i = 0; i < len; i++) {
m = this.res[this.rlen - 1] ^ data[i];
for (k = this.rlen - 1; k > 0; k--) {
if (m != 0 && this.rspoly[k] != 0) {
this.res[k] = this.res[k - 1] ^ this.alog[(this.logt[m] + this.logt[this.rspoly[k]]) % this.logmod];
} else {
this.res[k] = this.res[k - 1];
}
}
if (m != 0 && this.rspoly[0] != 0) {
this.res[0] = this.alog[(this.logt[m] + this.logt[this.rspoly[0]]) % this.logmod];
} else {
this.res[0] = 0;
}
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/CodablockF.java
New file
0,0 → 1,869
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import java.awt.geom.Rectangle2D;
import java.nio.charset.StandardCharsets;
 
/**
* <p>
* Implements Codablock-F according to AIM Europe "Uniform Symbology Specification - Codablock F",
* 1995.
*
* <p>
* Codablock-F is a multi-row symbology using Code 128 encoding. It can encode any 8-bit ISO 8859-1
* (Latin-1) data up to approximately 1000 alpha-numeric characters or 2000 numeric digits in
* length.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class CodablockF extends Symbol {
 
private enum Mode {
SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB
}
 
private enum CfMode {
MODEA, MODEB, MODEC
}
 
/* Annex A Table A.1 */
private static final String[] C_128_TABLE = { "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231",
"113222", "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121",
"111323", "131123", "131321", "112313", "132113", "132311", "211313", "231113", "231311", "112133", "112331", "132131", "113123", "113321", "133121", "313121", "211331", "231131",
"213113", "213311", "213131", "311123", "311321", "331121", "312113", "312311", "332111", "314111", "221411", "431111", "111224", "111422", "121124", "121421", "141122", "141221",
"112214", "112412", "122114", "122411", "142112", "142211", "241211", "221114", "413111", "241112", "134111", "111242", "121142", "121241", "114212", "124112", "124211", "411212",
"421112", "421211", "212141", "214121", "412121", "111143", "111341", "131141", "114113", "114311", "411113", "411311", "113141", "114131", "311141", "411131", "211412", "211214",
"211232", "2331112" };
 
private final int[][] blockmatrix = new int[44][62];
private int columns_needed;
private int rows_needed;
private CfMode final_mode;
private final CfMode[] subset_selector = new CfMode[44];
 
/**
* TODO: It doesn't appear that this symbol should support GS1 (it's not in the GS1 spec and
* Zint doesn't support GS1 with this type of symbology). However, the code below does contain
* GS1 checks, so we'll mark it as supported for now. It's very possible that the code below
* which supports GS1 only does so because it was originally copied from the Code 128 source
* code (just a suspicion, though).
*/
@Override
protected boolean gs1Supported() {
return true;
}
 
@Override
protected void encode() {
 
int input_length, i, j, k;
int min_module_height;
Mode last_mode, this_mode;
double estimate_codelength;
String row_pattern;
final int[] row_indicator = new int[44];
final int[] row_check = new int[44];
int k1_sum, k2_sum;
int k1_check, k2_check;
 
this.final_mode = CfMode.MODEA;
 
if (!this.content.matches("[\u0000-\u00FF]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
this.inputData = toBytes(this.content, StandardCharsets.ISO_8859_1, 0x00);
input_length = this.inputData.length - 1;
 
if (input_length > 5450) {
throw new OkapiException("Input data too long");
}
 
/* Make a guess at how many characters will be needed to encode the data */
estimate_codelength = 0.0;
last_mode = Mode.AORB; /* Codablock always starts with Code A */
for (i = 0; i < input_length; i++) {
this_mode = findSubset(this.inputData[i]);
if (this_mode != last_mode) {
estimate_codelength += 1.0;
}
if (this_mode != Mode.ABORC) {
estimate_codelength += 1.0;
} else {
estimate_codelength += 0.5;
}
if (this.inputData[i] > 127) {
estimate_codelength += 1.0;
}
last_mode = this_mode;
}
 
/* Decide symbol size based on the above guess */
this.rows_needed = (int) (0.5 + Math.sqrt((estimate_codelength + 2) / 1.45));
if (this.rows_needed < 2) {
this.rows_needed = 2;
}
if (this.rows_needed > 44) {
this.rows_needed = 44;
}
this.columns_needed = (int) (estimate_codelength + 2) / this.rows_needed;
if (this.columns_needed < 4) {
this.columns_needed = 4;
}
if (this.columns_needed > 62) {
throw new OkapiException("Input data too long");
}
 
/* Encode the data */
data_encode_blockf();
 
/* Add check digits - Annex F */
k1_sum = 0;
k2_sum = 0;
for (i = 0; i < input_length; i++) {
if (this.inputData[i] == FNC1) {
k1_sum += (i + 1) * 29; /* GS */
k2_sum += i * 29;
} else {
k1_sum += (i + 1) * this.inputData[i];
k2_sum += i * this.inputData[i];
}
}
k1_check = k1_sum % 86;
k2_check = k2_sum % 86;
if (this.final_mode == CfMode.MODEA || this.final_mode == CfMode.MODEB) {
k1_check = k1_check + 64;
if (k1_check > 95) {
k1_check -= 96;
}
k2_check = k2_check + 64;
if (k2_check > 95) {
k2_check -= 96;
}
}
this.blockmatrix[this.rows_needed - 1][this.columns_needed - 2] = k1_check;
this.blockmatrix[this.rows_needed - 1][this.columns_needed - 1] = k2_check;
 
/* Calculate row height (4.6.1.a) */
min_module_height = (int) (0.55 * (this.columns_needed + 3)) + 3;
if (min_module_height < 8) {
min_module_height = 8;
}
 
/* Encode the Row Indicator in the First Row of the Symbol - Table D2 */
if (this.subset_selector[0] == CfMode.MODEC) {
/* Code C */
row_indicator[0] = this.rows_needed - 2;
} else {
/* Code A or B */
row_indicator[0] = this.rows_needed + 62;
 
if (row_indicator[0] > 95) {
row_indicator[0] -= 95;
}
}
 
/* Encode the Row Indicator in the Second and Subsequent Rows of the Symbol - Table D3 */
for (i = 1; i < this.rows_needed; i++) {
/* Note that the second row is row number 1 because counting starts from 0 */
if (this.subset_selector[i] == CfMode.MODEC) {
/* Code C */
row_indicator[i] = i + 42;
} else {
/* Code A or B */
if (i < 6) {
row_indicator[i] = i + 10;
} else {
row_indicator[i] = i + 20;
}
}
}
 
/* Calculate row check digits - Annex E */
for (i = 0; i < this.rows_needed; i++) {
k = 103;
switch (this.subset_selector[i]) {
case MODEA:
k += 98;
break;
case MODEB:
k += 100;
break;
case MODEC:
k += 99;
break;
}
k += 2 * row_indicator[i];
for (j = 0; j < this.columns_needed; j++) {
k += (j + 3) * this.blockmatrix[i][j];
}
row_check[i] = k % 103;
}
 
this.readable = "";
this.row_count = this.rows_needed;
this.pattern = new String[this.row_count];
this.row_height = new int[this.row_count];
 
infoLine("Grid Size: " + this.columns_needed + " X " + this.rows_needed);
infoLine("K1 Check Digit: " + k1_check);
infoLine("K2 Check Digit: " + k2_check);
 
/* Resolve the data into patterns and place in symbol structure */
info("Encoding: ");
for (i = 0; i < this.rows_needed; i++) {
 
row_pattern = "";
/* Start character */
row_pattern += C_128_TABLE[103]; /* Always Start A */
 
switch (this.subset_selector[i]) {
case MODEA:
row_pattern += C_128_TABLE[98];
info("MODEA ");
break;
case MODEB:
row_pattern += C_128_TABLE[100];
info("MODEB ");
break;
case MODEC:
row_pattern += C_128_TABLE[99];
info("MODEC ");
break;
}
row_pattern += C_128_TABLE[row_indicator[i]];
infoSpace(row_indicator[i]);
 
for (j = 0; j < this.columns_needed; j++) {
row_pattern += C_128_TABLE[this.blockmatrix[i][j]];
infoSpace(this.blockmatrix[i][j]);
}
 
row_pattern += C_128_TABLE[row_check[i]];
info("(" + row_check[i] + ") ");
 
/* Stop character */
row_pattern += C_128_TABLE[106];
 
/* Write the information into the symbol */
this.pattern[i] = row_pattern;
this.row_height[i] = 15;
}
infoLine();
 
this.symbol_height = this.rows_needed * 15;
}
 
private Mode findSubset(final int letter) {
Mode mode;
 
if (letter == FNC1) {
mode = Mode.AORB;
} else if (letter <= 31) {
mode = Mode.SHIFTA;
} else if (letter >= 48 && letter <= 57) {
mode = Mode.ABORC;
} else if (letter <= 95) {
mode = Mode.AORB;
} else if (letter <= 127) {
mode = Mode.SHIFTB;
} else if (letter <= 159) {
mode = Mode.SHIFTA;
} else if (letter <= 223) {
mode = Mode.AORB;
} else {
mode = Mode.SHIFTB;
}
 
return mode;
}
 
private void data_encode_blockf() {
 
int i, j, input_position, current_row;
int column_position, c;
CfMode current_mode;
boolean done, exit_status;
final int input_length = this.inputData.length - 1;
 
exit_status = false;
current_row = 0;
current_mode = CfMode.MODEA;
column_position = 0;
input_position = 0;
c = 0;
 
do {
done = false;
/*
* 'done' ensures that the instructions are followed in the correct order for each input
* character
*/
 
if (column_position == 0) {
/* The Beginning of a row */
c = this.columns_needed;
current_mode = character_subset_select(input_position);
this.subset_selector[current_row] = current_mode;
if (current_row == 0 && this.inputDataType == DataType.GS1) {
/* Section 4.4.7.1 */
this.blockmatrix[current_row][column_position] = 102; /* FNC1 */
column_position++;
c--;
}
}
 
if (this.inputData[input_position] == FNC1) {
this.blockmatrix[current_row][column_position] = 102; /* FNC1 */
column_position++;
c--;
input_position++;
done = true;
}
 
if (!done) {
if (c <= 2) {
/* Annex B section 1 rule 1 */
/*
* Ensure that there is sufficient encodation capacity to continue (using the
* rules of Annex B.2).
*/
switch (current_mode) {
case MODEA: /* Table B1 applies */
if (findSubset(this.inputData[input_position]) == Mode.ABORC) {
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
 
if (findSubset(this.inputData[input_position]) == Mode.SHIFTB && c == 1) {
/* Needs two symbols */
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
done = true;
}
 
if (this.inputData[input_position] >= 244 && !done) {
/* Needs three symbols */
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
if (c == 1) {
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
}
done = true;
}
 
if (this.inputData[input_position] >= 128 && !done && c == 1) {
/* Needs two symbols */
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
done = true;
}
break;
case MODEB: /* Table B2 applies */
if (findSubset(this.inputData[input_position]) == Mode.ABORC) {
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
 
if (findSubset(this.inputData[input_position]) == Mode.SHIFTA && c == 1) {
/* Needs two symbols */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
done = true;
}
 
if (this.inputData[input_position] >= 128 && this.inputData[input_position] <= 159 && !done) {
/* Needs three symbols */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
if (c == 1) {
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
}
done = true;
}
 
if (this.inputData[input_position] >= 160 && !done && c == 1) {
/* Needs two symbols */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
done = true;
}
break;
case MODEC: /* Table B3 applies */
if (findSubset(this.inputData[input_position]) != Mode.ABORC && c == 1) {
/* Needs two symbols */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
done = true;
}
 
if (findSubset(this.inputData[input_position]) == Mode.ABORC && findSubset(this.inputData[input_position + 1]) != Mode.ABORC && c == 1) {
/* Needs two symbols */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
done = true;
}
 
if (this.inputData[input_position] >= 128) {
/* Needs three symbols */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
if (c == 1) {
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
}
}
break;
}
}
}
 
if (!done) {
if ((findSubset(this.inputData[input_position]) == Mode.AORB || findSubset(this.inputData[input_position]) == Mode.SHIFTA) && current_mode == CfMode.MODEA) {
/* Annex B section 1 rule 2 */
/*
* If in Code Subset A and the next data character can be encoded in Subset A
* encode the next character.
*/
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 101; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
}
 
if (!done) {
if ((findSubset(this.inputData[input_position]) == Mode.AORB || findSubset(this.inputData[input_position]) == Mode.SHIFTB) && current_mode == CfMode.MODEB) {
/* Annex B section 1 rule 3 */
/*
* If in Code Subset B and the next data character can be encoded in subset B,
* encode the next character.
*/
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 100; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
}
 
if (!done) {
if (findSubset(this.inputData[input_position]) == Mode.ABORC && findSubset(this.inputData[input_position + 1]) == Mode.ABORC && current_mode == CfMode.MODEC) {
/* Annex B section 1 rule 4 */
/* If in Code Subset C and the next data are 2 digits, encode them. */
this.blockmatrix[current_row][column_position] = (this.inputData[input_position] - '0') * 10 + this.inputData[input_position + 1] - '0';
column_position++;
c--;
input_position += 2;
done = true;
}
}
 
if (!done) {
if ((current_mode == CfMode.MODEA || current_mode == CfMode.MODEB) && (findSubset(this.inputData[input_position]) == Mode.ABORC || this.inputData[input_position] == FNC1)) {
// Count the number of numeric digits
// If 4 or more numeric data characters occur together when in subsets A or B:
// a. If there is an even number of numeric data characters, insert a Code C
// character before the
// first numeric digit to change to subset C.
// b. If there is an odd number of numeric data characters, insert a Code Set C
// character immediately
// after the first numeric digit to change to subset C.
i = 0;
j = 0;
do {
i++;
if (this.inputData[input_position + j] == FNC1) {
i++;
}
j++;
} while (findSubset(this.inputData[input_position + j]) == Mode.ABORC || this.inputData[input_position + j] == FNC1);
i--;
 
if (i >= 4) {
/* Annex B section 1 rule 5 */
if (i % 2 == 1) {
/* Annex B section 1 rule 5a */
this.blockmatrix[current_row][column_position] = 99; /* Code C */
column_position++;
c--;
this.blockmatrix[current_row][column_position] = (this.inputData[input_position] - '0') * 10 + this.inputData[input_position + 1] - '0';
column_position++;
c--;
input_position += 2;
current_mode = CfMode.MODEC;
} else {
/* Annex B section 1 rule 5b */
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
}
done = true;
} else {
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
done = true;
}
}
}
 
if (!done) {
if (current_mode == CfMode.MODEB && findSubset(this.inputData[input_position]) == Mode.SHIFTA) {
/* Annex B section 1 rule 6 */
/*
* When in subset B and an ASCII control character occurs in the data: a. If
* there is a lower case character immediately following the control character,
* insert a Shift character before the control character. b. Otherwise, insert a
* Code A character before the control character to change to subset A.
*/
if (this.inputData[input_position + 1] >= 96 && this.inputData[input_position + 1] <= 127) {
/* Annex B section 1 rule 6a */
this.blockmatrix[current_row][column_position] = 98; /* Shift */
column_position++;
c--;
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 100; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
} else {
/* Annex B section 1 rule 6b */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 100; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
current_mode = CfMode.MODEA;
}
done = true;
}
}
 
if (!done) {
if (current_mode == CfMode.MODEA && findSubset(this.inputData[input_position]) == Mode.SHIFTB) {
/* Annex B section 1 rule 7 */
/*
* When in subset A and a lower case character occurs in the data: a. If
* following that character, a control character occurs in the data before the
* occurrence of another lower case character, insert a Shift character before
* the lower case character. b. Otherwise, insert a Code B character before the
* lower case character to change to subset B.
*/
if (findSubset(this.inputData[input_position + 1]) == Mode.SHIFTA && findSubset(this.inputData[input_position + 2]) == Mode.SHIFTB) {
/* Annex B section 1 rule 7a */
this.blockmatrix[current_row][column_position] = 98; /* Shift */
column_position++;
c--;
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 101; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
} else {
/* Annex B section 1 rule 7b */
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 101; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
current_mode = CfMode.MODEB;
}
done = true;
}
}
 
if (!done) {
if (current_mode == CfMode.MODEC && (findSubset(this.inputData[input_position]) != Mode.ABORC || findSubset(this.inputData[input_position + 1]) != Mode.ABORC)) {
/* Annex B section 1 rule 8 */
/*
* When in subset C and a non-numeric character (or a single digit) occurs in
* the data, insert a Code A or Code B character before that character,
* following rules 8a and 8b to determine between code subsets A and B. a. If an
* ASCII control character (eg NUL) occurs in the data before any lower case
* character, use Code A. b. Otherwise use Code B.
*/
if (findSubset(this.inputData[input_position]) == Mode.SHIFTA) {
/* Annex B section 1 rule 8a */
this.blockmatrix[current_row][column_position] = 101; /* Code A */
column_position++;
c--;
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 101; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
current_mode = CfMode.MODEA;
} else {
/* Annex B section 1 rule 8b */
this.blockmatrix[current_row][column_position] = 100; /* Code B */
column_position++;
c--;
if (this.inputData[input_position] >= 128) {
/* Extended ASCII character */
this.blockmatrix[current_row][column_position] = 100; /* FNC4 */
column_position++;
c--;
}
this.blockmatrix[current_row][column_position] = a3_convert(this.inputData[input_position]);
column_position++;
c--;
input_position++;
current_mode = CfMode.MODEB;
}
done = true;
}
}
 
if (input_position == input_length) {
/* End of data - Annex B rule 5a */
if (c == 1) {
if (current_mode == CfMode.MODEA) {
this.blockmatrix[current_row][column_position] = 100; /* Code B */
current_mode = CfMode.MODEB;
} else {
this.blockmatrix[current_row][column_position] = 101; /* Code A */
current_mode = CfMode.MODEA;
}
column_position++;
c--;
}
 
if (c == 0) {
/* Another row is needed */
column_position = 0;
c = this.columns_needed;
current_row++;
this.subset_selector[current_row] = CfMode.MODEA;
current_mode = CfMode.MODEA;
}
 
if (c > 2) {
/* Fill up the last row */
do {
if (current_mode == CfMode.MODEA) {
this.blockmatrix[current_row][column_position] = 100; /* Code B */
current_mode = CfMode.MODEB;
} else {
this.blockmatrix[current_row][column_position] = 101; /* Code A */
current_mode = CfMode.MODEA;
}
column_position++;
c--;
} while (c > 2);
}
 
/* If (c == 2) { do nothing } */
 
exit_status = true;
this.final_mode = current_mode;
} else {
if (c <= 0) {
/* Start new row - Annex B rule 5b */
column_position = 0;
current_row++;
if (current_row > 43) {
throw new OkapiException("Too many rows.");
}
}
}
 
} while (!exit_status);
 
if (current_row == 0) {
/* fill up the first row */
for (c = column_position; c <= this.columns_needed; c++) {
if (current_mode == CfMode.MODEA) {
this.blockmatrix[current_row][c] = 100; /* Code B */
current_mode = CfMode.MODEB;
} else {
this.blockmatrix[current_row][c] = 101; /* Code A */
current_mode = CfMode.MODEA;
}
}
current_row++;
/* add a second row */
this.subset_selector[current_row] = CfMode.MODEA;
current_mode = CfMode.MODEA;
for (c = 0; c <= this.columns_needed - 2; c++) {
if (current_mode == CfMode.MODEA) {
this.blockmatrix[current_row][c] = 100; /* Code B */
current_mode = CfMode.MODEB;
} else {
this.blockmatrix[current_row][c] = 101; /* Code A */
current_mode = CfMode.MODEA;
}
}
}
 
this.rows_needed = current_row + 1;
}
 
private CfMode character_subset_select(final int input_position) {
 
/* Section 4.5.2 - Determining the Character Subset Selector in a Row */
 
if (this.inputData[input_position] >= '0' && this.inputData[input_position] <= '9') {
/* Rule 1 */
return CfMode.MODEC;
}
 
if (this.inputData[input_position] >= 128 && this.inputData[input_position] <= 160) {
/* Rule 2 (i) */
return CfMode.MODEA;
}
 
if (this.inputData[input_position] >= 0 && this.inputData[input_position] <= 31) {
/* Rule 3 */
return CfMode.MODEA;
}
 
/* Rule 4 */
return CfMode.MODEB;
}
 
private int a3_convert(final int source) {
/* Annex A section 3 */
if (source < 32) {
return source + 64;
}
if (source >= 32 && source <= 127) {
return source - 32;
}
if (source >= 128 && source <= 159) {
return source - 128 + 64;
}
/* if source >= 160 */
return source - 128 - 32;
}
 
@Override
protected void plotSymbol() {
int xBlock, yBlock;
int x, y, w, h;
boolean black;
 
this.rectangles.clear();
y = 1;
h = 1;
for (yBlock = 0; yBlock < this.row_count; yBlock++) {
black = true;
x = 0;
for (xBlock = 0; xBlock < this.pattern[yBlock].length(); xBlock++) {
if (black) {
black = false;
w = this.pattern[yBlock].charAt(xBlock) - '0';
if (this.row_height[yBlock] == -1) {
h = this.default_height;
} else {
h = this.row_height[yBlock];
}
if (w != 0 && h != 0) {
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
this.rectangles.add(rect);
}
if (x + w > this.symbol_width) {
this.symbol_width = x + w;
}
} else {
black = true;
}
x += this.pattern[yBlock].charAt(xBlock) - '0';
}
y += h;
if (y > this.symbol_height) {
this.symbol_height = y;
}
/* Add bars between rows */
if (yBlock != this.row_count - 1) {
final Rectangle2D.Double rect = new Rectangle2D.Double(11, y - 1, this.symbol_width - 24, 2);
this.rectangles.add(rect);
}
}
 
/* Add top and bottom binding bars */
final Rectangle2D.Double top = new Rectangle2D.Double(0, 0, this.symbol_width, 2);
this.rectangles.add(top);
final Rectangle2D.Double bottom = new Rectangle2D.Double(0, y - 1, this.symbol_width, 2);
this.rectangles.add(bottom);
this.symbol_height += 1;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/AustraliaPost.java
New file
0,0 → 1,481
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
import java.awt.geom.Rectangle2D;
 
/**
* Implements the <a href=
* "http://auspost.com.au/media/documents/a-guide-to-printing-the-4state-barcode-v31-mar2012.pdf">Australia
* Post 4-State barcode</a>.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class AustraliaPost extends Symbol {
 
private static final char[] CHARACTER_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ' ', '#' };
 
private static final String[] N_ENCODING_TABLE = { "00", "01", "02", "10", "11", "12", "20", "21", "22", "30" };
 
private static final String[] C_ENCODING_TABLE = { "222", "300", "301", "302", "310", "311", "312", "320", "321", "322", "000", "001", "002", "010", "011", "012", "020", "021", "022", "100",
"101", "102", "110", "111", "112", "120", "121", "122", "200", "201", "202", "210", "211", "212", "220", "221", "023", "030", "031", "032", "033", "103", "113", "123", "130", "131", "132",
"133", "203", "213", "223", "230", "231", "232", "233", "303", "313", "323", "330", "331", "332", "333", "003", "013" };
 
private static final String[] BAR_VALUE_TABLE = { "000", "001", "002", "003", "010", "011", "012", "013", "020", "021", "022", "023", "030", "031", "032", "033", "100", "101", "102", "103", "110",
"111", "112", "113", "120", "121", "122", "123", "130", "131", "132", "133", "200", "201", "202", "203", "210", "211", "212", "213", "220", "221", "222", "223", "230", "231", "232", "233",
"300", "301", "302", "303", "310", "311", "312", "313", "320", "321", "322", "323", "330", "331", "332", "333" };
 
private enum ausMode {
AUSPOST, AUSREPLY, AUSROUTE, AUSREDIRECT
}
 
private ausMode mode;
 
public AustraliaPost() {
this.mode = ausMode.AUSPOST;
}
 
/**
* Specify encoding of Australia Post Standard Customer Barcode, Customer Barcode 2 or Customer
* Barcode 3 (37-bar, 52-bar and 67-bar symbols) depending on input data length. Valid data
* characters are 0-9, A-Z, a-z, space and hash (#). A Format Control Code (FCC) is added and
* should not be included in the input data.
* <p>
* Input data should include a 8-digit Deliver Point ID (DPID) optionally followed by customer
* information as shown below.
* <table summary="Permitted Australia Post input data">
* <tbody>
* <tr>
* <th>
* <p>
* Input Length
* </p>
* </th>
* <th>
* <p>
* Required Input Format
* </p>
* </th>
* <th>
* <p>
* Symbol Length
* </p>
* </th>
* <th>
* <p>
* FCC
* </p>
* </th>
* <th>
* <p>
* Encoding Table
* </p>
* </th>
* </tr>
* <tr>
* <td>
* <p>
* 8
* </p>
* </td>
* <td>
* <p>
* 99999999
* </p>
* </td>
* <td>
* <p>
* 37-bar
* </p>
* </td>
* <td>
* <p>
* 11
* </p>
* </td>
* <td>
* <p>
* None
* </p>
* </td>
* </tr>
* <tr>
* <td>
* <p>
* 13
* </p>
* </td>
* <td>
* <p>
* 99999999AAAAA
* </p>
* </td>
* <td>
* <p>
* 52-bar
* </p>
* </td>
* <td>
* <p>
* 59
* </p>
* </td>
* <td>
* <p>
* C
* </p>
* </td>
* </tr>
* <tr>
* <td>
* <p>
* 16
* </p>
* </td>
* <td>
* <p>
* 9999999999999999
* </p>
* </td>
* <td>
* <p>
* 52-bar
* </p>
* </td>
* <td>
* <p>
* 59
* </p>
* </td>
* <td>
* <p>
* N
* </p>
* </td>
* </tr>
* <tr>
* <td>
* <p>
* 18
* </p>
* </td>
* <td>
* <p>
* 99999999AAAAAAAAAA
* </p>
* </td>
* <td>
* <p>
* 67-bar
* </p>
* </td>
* <td>
* <p>
* 62
* </p>
* </td>
* <td>
* <p>
* C
* </p>
* </td>
* </tr>
* <tr>
* <td>
* <p>
* 23
* </p>
* </td>
* <td>
* <p>
* 99999999999999999999999
* </p>
* </td>
* <td>
* <p>
* 67-bar
* </p>
* </td>
* <td>
* <p>
* 62
* </p>
* </td>
* <td>
* <p>
* N
* </p>
* </td>
* </tr>
* </tbody>
* </table>
*/
public void setPostMode() {
this.mode = ausMode.AUSPOST;
}
 
/**
* Specify encoding of a Reply Paid version of the Australia Post 4-State Barcode (FCC 45) which
* requires an 8-digit DPID input.
*/
public void setReplyMode() {
this.mode = ausMode.AUSREPLY;
}
 
/**
* Specify encoding of a Routing version of the Australia Post 4-State Barcode (FCC 87) which
* requires an 8-digit DPID input.
*/
public void setRouteMode() {
this.mode = ausMode.AUSROUTE;
}
 
/**
* Specify encoding of a Redirection version of the Australia Post 4-State Barcode (FCC 92)
* which requires an 8-digit DPID input.
*/
public void setRedirectMode() {
this.mode = ausMode.AUSREDIRECT;
}
 
/** {@inheritDoc} */
@Override
protected void encode() {
String formatControlCode = "00";
String deliveryPointId;
String barStateValues;
String zeroPaddedInput = "";
int i;
 
switch (this.mode) {
case AUSPOST:
switch (this.content.length()) {
case 8:
formatControlCode = "11";
break;
case 13:
formatControlCode = "59";
break;
case 16:
formatControlCode = "59";
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in data");
}
break;
case 18:
formatControlCode = "62";
break;
case 23:
formatControlCode = "62";
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in data");
}
break;
default:
throw new OkapiException("Auspost input is wrong length");
}
break;
case AUSREPLY:
if (this.content.length() > 8) {
throw new OkapiException("Auspost input is too long");
} else {
formatControlCode = "45";
}
break;
case AUSROUTE:
if (this.content.length() > 8) {
throw new OkapiException("Auspost input is too long");
} else {
formatControlCode = "87";
}
break;
case AUSREDIRECT:
if (this.content.length() > 8) {
throw new OkapiException("Auspost input is too long");
} else {
formatControlCode = "92";
}
break;
}
 
infoLine("FCC: " + formatControlCode);
 
if (this.mode != ausMode.AUSPOST) {
for (i = this.content.length(); i < 8; i++) {
zeroPaddedInput += "0";
}
}
zeroPaddedInput += this.content;
 
if (!this.content.matches("[0-9A-Za-z #]+")) {
throw new OkapiException("Invalid characters in data");
}
 
/* Verify that the first 8 characters are numbers */
deliveryPointId = zeroPaddedInput.substring(0, 8);
 
if (!deliveryPointId.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in DPID");
}
 
infoLine("DPID: " + deliveryPointId);
 
/* Start */
barStateValues = "13";
 
/* Encode the FCC */
for (i = 0; i < 2; i++) {
barStateValues += N_ENCODING_TABLE[formatControlCode.charAt(i) - '0'];
}
 
/* Delivery Point Identifier (DPID) */
for (i = 0; i < 8; i++) {
barStateValues += N_ENCODING_TABLE[deliveryPointId.charAt(i) - '0'];
}
 
/* Customer Information */
switch (zeroPaddedInput.length()) {
case 13:
case 18:
for (i = 8; i < zeroPaddedInput.length(); i++) {
barStateValues += C_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)];
}
break;
case 16:
case 23:
for (i = 8; i < zeroPaddedInput.length(); i++) {
barStateValues += N_ENCODING_TABLE[positionOf(zeroPaddedInput.charAt(i), CHARACTER_SET)];
}
break;
}
 
/* Filler bar */
switch (barStateValues.length()) {
case 22:
case 37:
case 52:
barStateValues += "3";
break;
}
 
/* Reed Solomon error correction */
barStateValues += calcReedSolomon(barStateValues);
 
/* Stop character */
barStateValues += "13";
 
infoLine("Total Length: " + barStateValues.length());
info("Encoding: ");
for (i = 0; i < barStateValues.length(); i++) {
switch (barStateValues.charAt(i)) {
case '1':
info("A");
break;
case '2':
info("D");
break;
case '0':
info("F");
break;
case '3':
info("T");
break;
}
}
infoLine();
 
this.readable = "";
this.pattern = new String[1];
this.pattern[0] = barStateValues;
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
}
 
private String calcReedSolomon(final String oldBarStateValues) {
final ReedSolomon rs = new ReedSolomon();
String newBarStateValues = "";
 
/* Adds Reed-Solomon error correction to auspost */
 
int barStateCount;
int tripleValueCount = 0;
final int[] tripleValue = new int[31];
 
for (barStateCount = 2; barStateCount < oldBarStateValues.length(); barStateCount += 3, tripleValueCount++) {
tripleValue[tripleValueCount] = barStateToDecimal(oldBarStateValues.charAt(barStateCount), 4) + barStateToDecimal(oldBarStateValues.charAt(barStateCount + 1), 2)
+ barStateToDecimal(oldBarStateValues.charAt(barStateCount + 2), 0);
}
 
rs.init_gf(0x43);
rs.init_code(4, 1);
rs.encode(tripleValueCount, tripleValue);
 
for (barStateCount = 4; barStateCount > 0; barStateCount--) {
newBarStateValues += BAR_VALUE_TABLE[rs.getResult(barStateCount - 1)];
}
 
return newBarStateValues;
}
 
private int barStateToDecimal(final char oldBarStateValues, final int shift) {
return oldBarStateValues - '0' << shift;
}
 
/** {@inheritDoc} */
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
 
this.rectangles.clear();
x = 0;
w = 1;
y = 0;
h = 0;
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
switch (this.pattern[0].charAt(xBlock)) {
case '1':
y = 0;
h = 5;
break;
case '2':
y = 3;
h = 5;
break;
case '0':
y = 0;
h = 8;
break;
case '3':
y = 3;
h = 2;
break;
}
 
final Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h);
this.rectangles.add(rect);
 
x += 2;
}
 
this.symbol_width = (this.pattern[0].length() - 1) * 2 + 1; // no whitespace needed after
// the final bar
this.symbol_height = 8;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Codabar.java
New file
0,0 → 1,95
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
/**
* <p>
* Implements Codabar barcode symbology according to BS EN 798:1996.
*
* <p>
* Also known as NW-7, Monarch, ABC Codabar, USD-4, Ames Code and Code 27. Codabar can encode any
* length string starting and ending with the letters A-D and containing between these letters the
* numbers 0-9, dash (-), dollar ($), colon (:), slash (/), full stop (.) or plus (+). No check
* digit is generated.
*
* @author <a href="mailto:jakel2006@me.com">Robert Elliott</a>
*/
public class Codabar extends Symbol {
 
private static final String[] CODABAR_TABLE = { "11111221", "11112211", "11121121", "22111111", "11211211", "21111211", "12111121", "12112111", "12211111", "21121111", "11122111", "11221111",
"21112121", "21211121", "21212111", "11212121", "11221211", "12121121", "11121221", "11122211" };
 
private static final char[] CHARACTER_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '$', ':', '/', '.', '+', 'A', 'B', 'C', 'D' };
 
/** Ratio of wide bar width to narrow bar width. */
private double moduleWidthRatio = 2;
 
/**
* Sets the ratio of wide bar width to narrow bar width. Valid values are usually between
* {@code 2} and {@code 3}. The default value is {@code 2}.
*
* @param moduleWidthRatio the ratio of wide bar width to narrow bar width
*/
public void setModuleWidthRatio(final double moduleWidthRatio) {
this.moduleWidthRatio = moduleWidthRatio;
}
 
/**
* Returns the ratio of wide bar width to narrow bar width.
*
* @return the ratio of wide bar width to narrow bar width
*/
public double getModuleWidthRatio() {
return this.moduleWidthRatio;
}
 
/** {@inheritDoc} */
@Override
protected void encode() {
 
if (!this.content.matches("[A-D]{1}[0-9:/\\$\\.\\+\u002D]+[A-D]{1}")) {
throw new OkapiException("Invalid characters in input");
}
 
String horizontalSpacing = "";
 
final int l = this.content.length();
for (int i = 0; i < l; i++) {
horizontalSpacing += CODABAR_TABLE[positionOf(this.content.charAt(i), CHARACTER_SET)];
}
 
this.readable = this.content;
this.pattern = new String[] { horizontalSpacing };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
/** {@inheritDoc} */
@Override
protected double getModuleWidth(final int originalWidth) {
if (originalWidth == 1) {
return 1;
} else {
return this.moduleWidthRatio;
}
}
 
/** {@inheritDoc} */
@Override
protected int[] getCodewords() {
return getPatternAsCodewords(8);
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Code3Of9Extended.java
New file
0,0 → 1,78
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
/**
* <p>
* Implements Code 3 of 9 Extended, also known as Code 39e and Code39+.
*
* <p>
* Supports encoding of all characters in the 7-bit ASCII table. A modulo-43 check digit can be
* added if required.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Code3Of9Extended extends Symbol {
 
private static final String[] E_CODE_39 = { "%U", "$A", "$B", "$C", "$D", "$E", "$F", "$G", "$H", "$I", "$J", "$K", "$L", "$M", "$N", "$O", "$P", "$Q", "$R", "$S", "$T", "$U", "$V", "$W", "$X",
"$Y", "$Z", "%A", "%B", "%C", "%D", "%E", " ", "/A", "/B", "/C", "/D", "/E", "/F", "/G", "/H", "/I", "/J", "/K", "/L", "-", ".", "/O", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"/Z", "%F", "%G", "%H", "%I", "%J", "%V", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "%K", "%L",
"%M", "%N", "%O", "%W", "+A", "+B", "+C", "+D", "+E", "+F", "+G", "+H", "+I", "+J", "+K", "+L", "+M", "+N", "+O", "+P", "+Q", "+R", "+S", "+T", "+U", "+V", "+W", "+X", "+Y", "+Z", "%P",
"%Q", "%R", "%S", "%T" };
 
public enum CheckDigit {
NONE, MOD43
}
 
private CheckDigit checkOption = CheckDigit.NONE;
 
/**
* Select addition of optional Modulo-43 check digit or encoding without check digit.
*
* @param checkMode check digit option
*/
public void setCheckDigit(final CheckDigit checkMode) {
this.checkOption = checkMode;
}
 
@Override
protected void encode() {
String buffer = "";
final int l = this.content.length();
int asciicode;
final Code3Of9 c = new Code3Of9();
 
if (this.checkOption == CheckDigit.MOD43) {
c.setCheckDigit(Code3Of9.CheckDigit.MOD43);
}
 
if (!this.content.matches("[\u0000-\u007F]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
for (int i = 0; i < l; i++) {
asciicode = this.content.charAt(i);
buffer += E_CODE_39[asciicode];
}
 
c.setContent(buffer);
 
this.readable = this.content;
this.pattern = new String[1];
this.pattern[0] = c.pattern[0];
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Postnet.java
New file
0,0 → 1,156
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.backend.HumanReadableLocation.NONE;
import static uk.org.okapibarcode.backend.HumanReadableLocation.TOP;
 
import java.awt.geom.Rectangle2D;
 
/**
* <p>
* Implements <a href="http://en.wikipedia.org/wiki/POSTNET">POSTNET</a> and
* <a href="http://en.wikipedia.org/wiki/Postal_Alpha_Numeric_Encoding_Technique">PLANET</a> bar
* code symbologies.
*
* <p>
* POSTNET and PLANET both use numerical input data and include a modulo-10 check digit.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Postnet extends Symbol {
 
public static enum Mode {
PLANET, POSTNET
};
 
private static final String[] PN_TABLE = { "LLSSS", "SSSLL", "SSLSL", "SSLLS", "SLSSL", "SLSLS", "SLLSS", "LSSSL", "LSSLS", "LSLSS" };
 
private static final String[] PL_TABLE = { "SSLLL", "LLLSS", "LLSLS", "LLSSL", "LSLLS", "LSLSL", "LSSLL", "SLLLS", "SLLSL", "SLSLL" };
 
private Mode mode;
 
public Postnet() {
this.mode = Mode.POSTNET;
this.default_height = 12;
this.humanReadableLocation = HumanReadableLocation.NONE;
}
 
/**
* Sets the barcode mode (PLANET or POSTNET). The default mode is POSTNET.
*
* @param mode the barcode mode (PLANET or POSTNET)
*/
public void setMode(final Mode mode) {
this.mode = mode;
}
 
/**
* Returns the barcode mode (PLANET or POSTNET). The default mode is POSTNET.
*
* @return the barcode mode (PLANET or POSTNET)
*/
public Mode getMode() {
return this.mode;
}
 
@Override
protected void encode() {
final String[] table = this.mode == Mode.POSTNET ? PN_TABLE : PL_TABLE;
encode(table);
}
 
private void encode(final String[] table) {
int i, sum, check_digit;
String dest;
 
if (this.content.length() > 38) {
throw new OkapiException("Input too long");
}
 
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in data");
}
 
sum = 0;
dest = "L";
 
for (i = 0; i < this.content.length(); i++) {
dest += table[this.content.charAt(i) - '0'];
sum += this.content.charAt(i) - '0';
}
 
check_digit = (10 - sum % 10) % 10;
infoLine("Check Digit: " + check_digit);
 
dest += table[check_digit];
dest += "L";
 
infoLine("Encoding: " + dest);
this.readable = this.content;
this.pattern = new String[] { dest };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
 
@Override
protected void plotSymbol() {
int xBlock, shortHeight;
double x, y, w, h;
 
this.rectangles.clear();
this.texts.clear();
 
int baseY;
if (this.humanReadableLocation == TOP) {
baseY = getTheoreticalHumanReadableHeight();
} else {
baseY = 0;
}
 
x = 0;
w = this.moduleWidth;
shortHeight = (int) (0.4 * this.default_height);
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
if (this.pattern[0].charAt(xBlock) == 'L') {
y = baseY;
h = this.default_height;
} else {
y = baseY + this.default_height - shortHeight;
h = shortHeight;
}
this.rectangles.add(new Rectangle2D.Double(x, y, w, h));
x += 2.5 * w;
}
 
this.symbol_width = (int) Math.ceil((this.pattern[0].length() - 1) * 2.5 * w + w); // final
// bar
// doesn't
// need
// extra
// whitespace
this.symbol_height = this.default_height;
 
if (this.humanReadableLocation != NONE && !this.readable.isEmpty()) {
double baseline;
if (this.humanReadableLocation == TOP) {
baseline = this.fontSize;
} else {
baseline = this.symbol_height + this.fontSize;
}
this.texts.add(new TextBox(0, baseline, this.symbol_width, this.readable, this.humanReadableAlignment));
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Telepen.java
New file
0,0 → 1,166
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
/**
* <p>
* Implements Telepen (also known as Telepen Alpha).
*
* <p>
* Telepen can encode ASCII text input and includes a modulo-127 check digit. Telepen Numeric allows
* compression of numeric data into a Telepen symbol. Data can consist of pairs of numbers or pairs
* consisting of a numerical digit followed by an X character. Telepen Numeric also includes a
* modulo-127 check digit.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Telepen extends Symbol {
 
public static enum Mode {
NORMAL, NUMERIC
}
 
private static final String[] TELE_TABLE = { "1111111111111111", "1131313111", "33313111", "1111313131", "3111313111", "11333131", "13133131", "111111313111", "31333111", "1131113131", "33113131",
"1111333111", "3111113131", "1113133111", "1311133111", "111111113131", "3131113111", "11313331", "333331", "111131113111", "31113331", "1133113111", "1313113111", "1111113331",
"31131331", "113111113111", "3311113111", "1111131331", "311111113111", "1113111331", "1311111331", "11111111113111", "31313311", "1131311131", "33311131", "1111313311", "3111311131",
"11333311", "13133311", "111111311131", "31331131", "1131113311", "33113311", "1111331131", "3111113311", "1113131131", "1311131131", "111111113311", "3131111131", "1131131311",
"33131311", "111131111131", "3111131311", "1133111131", "1313111131", "111111131311", "3113111311", "113111111131", "3311111131", "111113111311", "311111111131", "111311111311",
"131111111311", "11111111111131", "3131311111", "11313133", "333133", "111131311111", "31113133", "1133311111", "1313311111", "1111113133", "313333", "113111311111", "3311311111",
"11113333", "311111311111", "11131333", "13111333", "11111111311111", "31311133", "1131331111", "33331111", "1111311133", "3111331111", "11331133", "13131133", "111111331111",
"3113131111", "1131111133", "33111133", "111113131111", "3111111133", "111311131111", "131111131111", "111111111133", "31311313", "113131111111", "3331111111", "1111311313",
"311131111111", "11331313", "13131313", "11111131111111", "3133111111", "1131111313", "33111313", "111133111111", "3111111313", "111313111111", "131113111111", "111111111313",
"313111111111", "1131131113", "33131113", "11113111111111", "3111131113", "113311111111", "131311111111", "111111131113", "3113111113", "11311111111111", "331111111111", "111113111113",
"31111111111111", "111311111113", "131111111113" };
 
private Mode mode = Mode.NORMAL;
 
public void setMode(final Mode mode) {
this.mode = mode;
}
 
public Mode getMode() {
return this.mode;
}
 
@Override
protected void encode() {
if (this.mode == Mode.NORMAL) {
normal_mode();
} else {
numeric_mode();
}
}
 
private void normal_mode() {
int count = 0, asciicode, check_digit;
String p = "";
String dest;
 
final int l = this.content.length();
 
if (!this.content.matches("[\u0000-\u007F]+")) {
throw new OkapiException("Invalid characters in input data");
}
 
dest = TELE_TABLE['_']; // Start
for (int i = 0; i < l; i++) {
asciicode = this.content.charAt(i);
p += TELE_TABLE[asciicode];
count += asciicode;
}
 
check_digit = 127 - count % 127;
if (check_digit == 127) {
check_digit = 0;
}
 
p += TELE_TABLE[check_digit];
 
infoLine("Check Digit: " + check_digit);
 
dest += p;
dest += TELE_TABLE['z']; // Stop
 
this.readable = this.content;
this.pattern = new String[1];
this.pattern[0] = dest;
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
}
 
private void numeric_mode() {
int count = 0, check_digit;
String p = "";
String t;
String dest;
final int l = this.content.length();
int tl, glyph;
char c1, c2;
 
// FIXME: Ensure no extended ASCII or Unicode characters are entered
if (!this.content.matches("[0-9X]+")) {
throw new OkapiException("Invalid characters in input");
}
 
/* If input is an odd length, add a leading zero */
if ((l & 1) == 1) {
t = "0" + this.content;
tl = l + 1;
} else {
t = this.content;
tl = l;
}
 
dest = TELE_TABLE['_']; // Start
for (int i = 0; i < tl; i += 2) {
 
c1 = t.charAt(i);
c2 = t.charAt(i + 1);
 
/* Input nX is allowed, but Xn is not */
if (c1 == 'X') {
throw new OkapiException("Invalid position of X in data");
}
 
if (c2 == 'X') {
glyph = c1 - '0' + 17;
count += glyph;
} else {
glyph = 10 * (c1 - '0') + c2 - '0' + 27;
count += glyph;
}
 
p += TELE_TABLE[glyph];
}
 
check_digit = 127 - count % 127;
if (check_digit == 127) {
check_digit = 0;
}
 
p += TELE_TABLE[check_digit];
 
infoLine("Check Digit: " + check_digit);
 
dest += p;
dest += TELE_TABLE['z']; // Stop
this.readable = this.content;
this.pattern = new String[1];
this.pattern[0] = dest;
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/TextBox.java
New file
0,0 → 1,62
/*
* Copyright 2014-2018 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
/**
* A simple text item class.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class TextBox {
 
/** The X position of the text's left boundary. */
public final double x;
 
/** The Y position of the text baseline. */
public final double y;
 
/** The width of the text box. */
public final double width;
 
/** The text value. */
public final String text;
 
/** The text alignment. */
public final HumanReadableAlignment alignment;
 
/**
* Creates a new instance.
*
* @param