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 Label/.settings/org.eclipse.jdt.core.prefs
New file
0,0 → 1,7
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8
/trunk/Modules/Module Label/.classpath
1,7 → 1,7
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry combineaccessrules="false" kind="src" path="/OpenConcerto"/>
<classpathentry kind="output" path="bin"/>
</classpath>
/trunk/Modules/Module Label/lib/barcode4j-2.1.0.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/lib/barcode4j-2.1.0.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/lib/jbarcode-0.2.8.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/Modules/Module Label/lib/jbarcode-0.2.8.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/ExtendedOutputStreamWriter.java
New file
0,0 → 1,80
/*
* Copyright 2015 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
 
/**
* {@link OutputStreamWriter} extension which provides some convenience methods for writing numbers.
*/
class ExtendedOutputStreamWriter extends OutputStreamWriter {
 
/** Format to use when writing doubles to the stream. */
private final String doubleFormat;
 
/**
* Creates a new extended output stream writer, using the UTF-8 charset.
*
* @param out the stream to write to
* @param doubleFormat the format to use when writing doubles to the stream
*/
public ExtendedOutputStreamWriter(final OutputStream out, final String doubleFormat) {
super(out, StandardCharsets.UTF_8);
this.doubleFormat = doubleFormat;
}
 
/** {@inheritDoc} */
@Override
public ExtendedOutputStreamWriter append(final CharSequence cs) throws IOException {
super.append(cs);
return this;
}
 
/** {@inheritDoc} */
@Override
public ExtendedOutputStreamWriter append(final CharSequence cs, final int start, final int end) throws IOException {
super.append(cs, start, end);
return this;
}
 
/**
* Writes the specified double to the stream, formatted according to the format specified in the
* constructor.
*
* @param d the double to write to the stream
* @return this writer
* @throws IOException if an I/O error occurs
*/
public ExtendedOutputStreamWriter append(final double d) throws IOException {
super.append(String.format(Locale.ROOT, this.doubleFormat, d));
return this;
}
 
/**
* Writes the specified integer to the stream.
*
* @param i the integer to write to the stream
* @return this writer
* @throws IOException if an I/O error occurs
*/
public ExtendedOutputStreamWriter appendInt(final int i) throws IOException {
super.append(String.valueOf(i));
return this;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/SymbolRenderer.java
New file
0,0 → 1,34
/*
* Copyright 2015 Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import java.io.IOException;
 
import uk.org.okapibarcode.backend.Symbol;
 
/**
* Renders symbols to some output format.
*/
public interface SymbolRenderer {
 
/**
* Renders the specified symbology.
*
* @param symbol the symbology to render
* @throws IOException if there is an I/O error
*/
void render(Symbol symbol) throws IOException;
 
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/Java2DRenderer.java
New file
0,0 → 1,169
/*
* Copyright 2014-2015 Robin Stuart, Robert Elliott, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER;
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY;
 
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.Collections;
import java.util.List;
 
import uk.org.okapibarcode.backend.Hexagon;
import uk.org.okapibarcode.backend.HumanReadableAlignment;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.backend.TextBox;
 
/**
* Renders symbologies using the Java 2D API.
*/
public class Java2DRenderer implements SymbolRenderer {
 
/** The graphics to render to. */
private final Graphics2D g2d;
 
/** The magnification factor to apply. */
private final double magnification;
 
/** The paper (background) color. */
private final Color paper;
 
/** The ink (foreground) color. */
private final Color ink;
 
/**
* Creates a new Java 2D renderer. If the specified paper color is <tt>null</tt>, the symbol is
* drawn without clearing the existing <tt>g2d</tt> background.
*
* @param g2d the graphics to render to
* @param magnification the magnification factor to apply
* @param paper the paper (background) color (may be <tt>null</tt>)
* @param ink the ink (foreground) color
*/
public Java2DRenderer(final Graphics2D g2d, final double magnification, final Color paper, final Color ink) {
this.g2d = g2d;
this.magnification = magnification;
this.paper = paper;
this.ink = ink;
}
 
/** {@inheritDoc} */
@Override
public void render(final Symbol symbol) {
 
final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification);
final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification);
 
Font f = symbol.getFont();
if (f != null) {
f = f.deriveFont((float) (f.getSize2D() * this.magnification));
} else {
f = new Font(symbol.getFontName(), Font.PLAIN, (int) (symbol.getFontSize() * this.magnification));
f = f.deriveFont(Collections.singletonMap(TextAttribute.TRACKING, 0));
}
 
final Font oldFont = this.g2d.getFont();
final Color oldColor = this.g2d.getColor();
 
if (this.paper != null) {
final int w = (int) (symbol.getWidth() * this.magnification);
final int h = (int) (symbol.getHeight() * this.magnification);
this.g2d.setColor(this.paper);
this.g2d.fillRect(0, 0, w, h);
}
 
this.g2d.setColor(this.ink);
 
for (final Rectangle2D.Double rect : symbol.getRectangles()) {
final double x = rect.x * this.magnification + marginX;
final double y = rect.y * this.magnification + marginY;
final double w = rect.width * this.magnification;
final double h = rect.height * this.magnification;
this.g2d.fillRect((int) x, (int) y, (int) w, (int) h);
}
 
for (final TextBox text : symbol.getTexts()) {
final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment;
final Font font = alignment != JUSTIFY ? f : addTracking(f, text.width * this.magnification, text.text, this.g2d);
this.g2d.setFont(font);
final FontMetrics fm = this.g2d.getFontMetrics();
final Rectangle2D bounds = fm.getStringBounds(text.text, this.g2d);
final float y = (float) (text.y * this.magnification) + marginY;
float x;
switch (alignment) {
case LEFT:
case JUSTIFY:
x = (float) (this.magnification * text.x + marginX);
break;
case RIGHT:
x = (float) (this.magnification * text.x + this.magnification * text.width - bounds.getWidth() + marginX);
break;
case CENTER:
x = (float) (this.magnification * text.x + this.magnification * text.width / 2 - bounds.getWidth() / 2 + marginX);
break;
default:
throw new IllegalStateException("Unknown alignment: " + alignment);
}
this.g2d.drawString(text.text, x, y);
}
 
for (final Hexagon hexagon : symbol.getHexagons()) {
final Polygon polygon = new Polygon();
for (int j = 0; j < 6; j++) {
polygon.addPoint((int) (hexagon.pointX[j] * this.magnification + marginX), (int) (hexagon.pointY[j] * this.magnification + marginY));
}
this.g2d.fill(polygon);
}
 
final List<Ellipse2D.Double> target = symbol.getTarget();
for (int i = 0; i + 1 < target.size(); i += 2) {
final Ellipse2D.Double outer = adjust(target.get(i), this.magnification, marginX, marginY);
final Ellipse2D.Double inner = adjust(target.get(i + 1), this.magnification, marginX, marginY);
final Area area = new Area(outer);
area.subtract(new Area(inner));
this.g2d.fill(area);
}
 
this.g2d.setFont(oldFont);
this.g2d.setColor(oldColor);
}
 
private static Ellipse2D.Double adjust(final Ellipse2D.Double ellipse, final double magnification, final int marginX, final int marginY) {
final double x = ellipse.x * magnification + marginX;
final double y = ellipse.y * magnification + marginY;
final double w = ellipse.width * magnification + marginX;
final double h = ellipse.height * magnification + marginY;
return new Ellipse2D.Double(x, y, w, h);
}
 
private static Font addTracking(final Font baseFont, final double maxTextWidth, final String text, final Graphics2D g2d) {
final FontRenderContext frc = g2d.getFontRenderContext();
final double originalWidth = baseFont.getStringBounds(text, frc).getWidth();
final double extraSpace = maxTextWidth - originalWidth;
final double extraSpacePerGap = extraSpace / (text.length() - 1);
final double scaleX = baseFont.isTransformed() ? baseFont.getTransform().getScaleX() : 1;
final double tracking = extraSpacePerGap / (baseFont.getSize2D() * scaleX);
return baseFont.deriveFont(Collections.singletonMap(TextAttribute.TRACKING, tracking));
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/PostScriptRenderer.java
New file
0,0 → 1,211
/*
* Copyright 2015 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER;
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY;
import static uk.org.okapibarcode.util.Doubles.roughlyEqual;
 
import java.awt.Color;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.OutputStream;
 
import uk.org.okapibarcode.backend.Hexagon;
import uk.org.okapibarcode.backend.HumanReadableAlignment;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.backend.TextBox;
 
/**
* Renders symbologies to EPS (Encapsulated PostScript).
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class PostScriptRenderer implements SymbolRenderer {
 
/** The output stream to render to. */
private final OutputStream out;
 
/** The magnification factor to apply. */
private final double magnification;
 
/** The paper (background) color. */
private final Color paper;
 
/** The ink (foreground) color. */
private final Color ink;
 
/**
* Creates a new PostScript renderer.
*
* @param out the output stream to render to
* @param magnification the magnification factor to apply
* @param paper the paper (background) color
* @param ink the ink (foreground) color
*/
public PostScriptRenderer(final OutputStream out, final double magnification, final Color paper, final Color ink) {
this.out = out;
this.magnification = magnification;
this.paper = paper;
this.ink = ink;
}
 
/** {@inheritDoc} */
@Override
public void render(final Symbol symbol) throws IOException {
 
// All y dimensions are reversed because EPS origin (0,0) is at the bottom left, not top
// left
 
final String content = symbol.getContent();
final int width = (int) (symbol.getWidth() * this.magnification);
final int height = (int) (symbol.getHeight() * this.magnification);
final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification);
final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification);
 
String title;
if (content == null || content.isEmpty()) {
title = "OkapiBarcode Generated Symbol";
} else {
title = content;
}
 
try (ExtendedOutputStreamWriter writer = new ExtendedOutputStreamWriter(this.out, "%.2f")) {
 
// Header
writer.append("%!PS-Adobe-3.0 EPSF-3.0\n");
writer.append("%%Creator: OkapiBarcode\n");
writer.append("%%Title: ").append(title).append('\n');
writer.append("%%Pages: 0\n");
writer.append("%%BoundingBox: 0 0 ").appendInt(width).append(" ").appendInt(height).append("\n");
writer.append("%%EndComments\n");
 
// Definitions
writer.append("/TL { setlinewidth moveto lineto stroke } bind def\n");
writer.append("/TC { moveto 0 360 arc 360 0 arcn fill } bind def\n");
writer.append("/TH { 0 setlinewidth moveto lineto lineto lineto lineto lineto closepath fill } bind def\n");
writer.append("/TB { 2 copy } bind def\n");
writer.append("/TR { newpath 4 1 roll exch moveto 1 index 0 rlineto 0 exch rlineto neg 0 rlineto closepath fill } bind def\n");
writer.append("/TE { pop pop } bind def\n");
 
// Background
writer.append("newpath\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(this.paper.getRed() / 255.0).append(" ").append(this.paper.getGreen() / 255.0).append(" ").append(this.paper.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(height).append(" 0.00 TB 0.00 ").append(width).append(" TR\n");
 
// Rectangles
for (int i = 0; i < symbol.getRectangles().size(); i++) {
final Rectangle2D.Double rect = symbol.getRectangles().get(i);
if (i == 0) {
writer.append("TE\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(rect.height * this.magnification).append(" ").append(height - (rect.y + rect.height) * this.magnification - marginY).append(" TB ")
.append(rect.x * this.magnification + marginX).append(" ").append(rect.width * this.magnification).append(" TR\n");
} else {
final Rectangle2D.Double prev = symbol.getRectangles().get(i - 1);
if (!roughlyEqual(rect.height, prev.height) || !roughlyEqual(rect.y, prev.y)) {
writer.append("TE\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(rect.height * this.magnification).append(" ").append(height - (rect.y + rect.height) * this.magnification - marginY).append(" ");
}
writer.append("TB ").append(rect.x * this.magnification + marginX).append(" ").append(rect.width * this.magnification).append(" TR\n");
}
}
 
// Text
for (int i = 0; i < symbol.getTexts().size(); i++) {
final TextBox text = symbol.getTexts().get(i);
final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment;
if (i == 0) {
writer.append("TE\n");
;
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
}
writer.append("matrix currentmatrix\n");
writer.append("/").append(symbol.getFontName()).append(" findfont\n");
writer.append(symbol.getFontSize() * this.magnification).append(" scalefont setfont\n");
final double y = height - text.y * this.magnification - marginY;
switch (alignment) {
case LEFT:
final double leftX = this.magnification * text.x + marginX;
writer.append(" 0 0 moveto ").append(leftX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
writer.append(" (").append(text.text).append(") show\n");
break;
case JUSTIFY:
final double textX = this.magnification * text.x + marginX;
final double textW = this.magnification * text.width;
writer.append(" 0 0 moveto ").append(textX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
writer.append(" (").append(text.text).append(") dup stringwidth pop ").append(textW).append(" sub neg 1 index length 1 sub div 0").append(" 3 -1 roll ashow\n");
break;
case RIGHT:
final double rightX = this.magnification * text.x + this.magnification * text.width + marginX;
writer.append(" 0 0 moveto ").append(rightX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
writer.append(" (").append(text.text).append(") stringwidth\n");
writer.append("pop\n");
writer.append("-1 mul 0 rmoveto\n");
writer.append(" (").append(text.text).append(") show\n");
break;
case CENTER:
final double centerX = this.magnification * text.x + this.magnification * text.width / 2 + marginX;
writer.append(" 0 0 moveto ").append(centerX).append(" ").append(y).append(" translate 0.00 rotate 0 0 moveto\n");
writer.append(" (").append(text.text).append(") stringwidth\n");
writer.append("pop\n");
writer.append("-2 div 0 rmoveto\n");
writer.append(" (").append(text.text).append(") show\n");
break;
default:
throw new IllegalStateException("Unknown alignment: " + alignment);
}
writer.append("setmatrix\n");
}
 
// Circles
// Because MaxiCode size is fixed, this ignores magnification
for (int i = 0; i < symbol.getTarget().size(); i += 2) {
final Ellipse2D.Double ellipse1 = symbol.getTarget().get(i);
final Ellipse2D.Double ellipse2 = symbol.getTarget().get(i + 1);
if (i == 0) {
writer.append("TE\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
writer.append(this.ink.getRed() / 255.0).append(" ").append(this.ink.getGreen() / 255.0).append(" ").append(this.ink.getBlue() / 255.0).append(" setrgbcolor\n");
}
final double x1 = ellipse1.x + ellipse1.width / 2;
final double x2 = ellipse2.x + ellipse2.width / 2;
final double y1 = height - ellipse1.y - ellipse1.width / 2;
final double y2 = height - ellipse2.y - ellipse2.width / 2;
final double r1 = ellipse1.width / 2;
final double r2 = ellipse2.width / 2;
writer.append(x1 + marginX).append(" ").append(y1 - marginY).append(" ").append(r1).append(" ").append(x2 + marginX).append(" ").append(y2 - marginY).append(" ").append(r2).append(" ")
.append(x2 + r2 + marginX).append(" ").append(y2 - marginY).append(" TC\n");
}
 
// Hexagons
// Because MaxiCode size is fixed, this ignores magnification
for (int i = 0; i < symbol.getHexagons().size(); i++) {
final Hexagon hexagon = symbol.getHexagons().get(i);
for (int j = 0; j < 6; j++) {
writer.append(hexagon.pointX[j] + marginX).append(" ").append(height - hexagon.pointY[j] - marginY).append(" ");
}
writer.append(" TH\n");
}
 
// Footer
writer.append("\nshowpage\n");
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/output/SvgRenderer.java
New file
0,0 → 1,225
/*
* Copyright 2014-2015 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.output;
 
import static uk.org.okapibarcode.backend.HumanReadableAlignment.CENTER;
import static uk.org.okapibarcode.backend.HumanReadableAlignment.JUSTIFY;
 
import java.awt.Color;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
 
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
 
import org.w3c.dom.Document;
import org.w3c.dom.Text;
 
import uk.org.okapibarcode.backend.Hexagon;
import uk.org.okapibarcode.backend.HumanReadableAlignment;
import uk.org.okapibarcode.backend.Symbol;
import uk.org.okapibarcode.backend.TextBox;
 
/**
* Renders symbologies to SVG (Scalable Vector Graphics).
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class SvgRenderer implements SymbolRenderer {
 
/** The output stream to render to. */
private final OutputStream out;
 
/** The magnification factor to apply. */
private final double magnification;
 
/** The paper (background) color. */
private final Color paper;
 
/** The ink (foreground) color. */
private final Color ink;
 
/** Whether or not to include the XML prolog in the output. */
private final boolean xmlProlog;
 
/**
* Creates a new SVG renderer.
*
* @param out the output stream to render to
* @param magnification the magnification factor to apply
* @param paper the paper (background) color
* @param ink the ink (foreground) color
* @param xmlProlog whether or not to include the XML prolog in the output (usually {@code true}
* for standalone SVG documents, {@code false} for SVG content embedded directly in HTML
* documents)
*/
public SvgRenderer(final OutputStream out, final double magnification, final Color paper, final Color ink, final boolean xmlProlog) {
this.out = out;
this.magnification = magnification;
this.paper = paper;
this.ink = ink;
this.xmlProlog = xmlProlog;
}
 
/** {@inheritDoc} */
@Override
public void render(final Symbol symbol) throws IOException {
 
final String content = symbol.getContent();
final int width = (int) (symbol.getWidth() * this.magnification);
final int height = (int) (symbol.getHeight() * this.magnification);
final int marginX = (int) (symbol.getQuietZoneHorizontal() * this.magnification);
final int marginY = (int) (symbol.getQuietZoneVertical() * this.magnification);
 
String title;
if (content == null || content.isEmpty()) {
title = "OkapiBarcode Generated Symbol";
} else {
title = content;
}
 
final String fgColour = String.format("%02X", this.ink.getRed()) + String.format("%02X", this.ink.getGreen()) + String.format("%02X", this.ink.getBlue());
 
final String bgColour = String.format("%02X", this.paper.getRed()) + String.format("%02X", this.paper.getGreen()) + String.format("%02X", this.paper.getBlue());
 
try (ExtendedOutputStreamWriter writer = new ExtendedOutputStreamWriter(this.out, "%.2f")) {
 
// XML Prolog
if (this.xmlProlog) {
writer.append("<?xml version=\"1.0\" standalone=\"no\"?>\n");
writer.append("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n");
writer.append(" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
}
 
// Header
writer.append("<svg width=\"").appendInt(width).append("\" height=\"").appendInt(height).append("\" version=\"1.1").append("\" xmlns=\"http://www.w3.org/2000/svg\">\n");
writer.append(" <desc>").append(clean(title)).append("</desc>\n");
writer.append(" <g id=\"barcode\" fill=\"#").append(fgColour).append("\">\n");
writer.append(" <rect x=\"0\" y=\"0\" width=\"").appendInt(width).append("\" height=\"").appendInt(height).append("\" fill=\"#").append(bgColour).append("\" />\n");
 
// Rectangles
for (int i = 0; i < symbol.getRectangles().size(); i++) {
final Rectangle2D.Double rect = symbol.getRectangles().get(i);
writer.append(" <rect x=\"").append(rect.x * this.magnification + marginX).append("\" y=\"").append(rect.y * this.magnification + marginY).append("\" width=\"")
.append(rect.width * this.magnification).append("\" height=\"").append(rect.height * this.magnification).append("\" />\n");
}
 
// Text
for (int i = 0; i < symbol.getTexts().size(); i++) {
final TextBox text = symbol.getTexts().get(i);
final HumanReadableAlignment alignment = text.alignment == JUSTIFY && text.text.length() == 1 ? CENTER : text.alignment;
double x;
String anchor;
switch (alignment) {
case LEFT:
case JUSTIFY:
x = this.magnification * text.x + marginX;
anchor = "start";
break;
case RIGHT:
x = this.magnification * text.x + this.magnification * text.width + marginX;
anchor = "end";
break;
case CENTER:
x = this.magnification * text.x + this.magnification * text.width / 2 + marginX;
anchor = "middle";
break;
default:
throw new IllegalStateException("Unknown alignment: " + alignment);
}
writer.append(" <text x=\"").append(x).append("\" y=\"").append(text.y * this.magnification + marginY).append("\" text-anchor=\"").append(anchor).append("\"\n");
if (alignment == JUSTIFY) {
writer.append(" textLength=\"").append(text.width * this.magnification).append("\" lengthAdjust=\"spacing\"\n");
}
writer.append(" font-family=\"").append(clean(symbol.getFontName())).append("\" font-size=\"").append(symbol.getFontSize() * this.magnification).append("\" fill=\"#")
.append(fgColour).append("\">\n");
writer.append(" ").append(clean(text.text)).append("\n");
writer.append(" </text>\n");
}
 
// Circles
for (int i = 0; i < symbol.getTarget().size(); i++) {
final Ellipse2D.Double ellipse = symbol.getTarget().get(i);
String color;
if ((i & 1) == 0) {
color = fgColour;
} else {
color = bgColour;
}
writer.append(" <circle cx=\"").append((ellipse.x + ellipse.width / 2) * this.magnification + marginX).append("\" cy=\"")
.append((ellipse.y + ellipse.width / 2) * this.magnification + marginY).append("\" r=\"").append(ellipse.width / 2 * this.magnification).append("\" fill=\"#").append(color)
.append("\" />\n");
}
 
// Hexagons
for (int i = 0; i < symbol.getHexagons().size(); i++) {
final Hexagon hexagon = symbol.getHexagons().get(i);
writer.append(" <path d=\"");
for (int j = 0; j < 6; j++) {
if (j == 0) {
writer.append("M ");
} else {
writer.append("L ");
}
writer.append(hexagon.pointX[j] * this.magnification + marginX).append(" ").append(hexagon.pointY[j] * this.magnification + marginY).append(" ");
}
writer.append("Z\" />\n");
}
 
// Footer
writer.append(" </g>\n");
writer.append("</svg>\n");
}
}
 
/**
* Cleans / sanitizes the specified string for inclusion in XML. A bit convoluted, but we're
* trying to do it without adding an external dependency just for this...
*
* @param s the string to be cleaned / sanitized
* @return the cleaned / sanitized string
*/
protected String clean(String s) {
 
// remove control characters
s = s.replaceAll("[\u0000-\u001f]", "");
 
// escape XML characters
try {
final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
final Text text = document.createTextNode(s);
final Transformer transformer = TransformerFactory.newInstance().newTransformer();
final DOMSource source = new DOMSource(text);
final StringWriter writer = new StringWriter();
final StreamResult result = new StreamResult(writer);
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(source, result);
return writer.toString();
} catch (ParserConfigurationException | TransformerException | TransformerFactoryConfigurationError e) {
return s;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/MsiPlessey.java
New file
0,0 → 1,205
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
/**
* <p>
* Implements the MSI (Modified Plessey) bar code symbology.
*
* <p>
* MSI Plessey can encode a string of numeric digits and has a range of check digit options.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class MsiPlessey extends Symbol {
 
public enum CheckDigit {
NONE, MOD10, MOD10_MOD10, MOD11, MOD11_MOD10
}
 
private final static String[] MSI_PLESS_TABLE = { "12121212", "12121221", "12122112", "12122121", "12211212", "12211221", "12212112", "12212121", "21121212", "21121221" };
 
private CheckDigit checkDigit = CheckDigit.NONE;
 
/**
* Set the check digit scheme to use. Options are: None, Modulo-10, 2 x Modulo-10, Modulo-11 and
* Modulo-11 &amp; 10.
*
* @param checkDigit the type of check digit to add to symbol
*/
public void setCheckDigit(final CheckDigit checkDigit) {
this.checkDigit = checkDigit;
}
 
/**
* Returns the check digit scheme being used.
*
* @return the check digit scheme being used
*/
public CheckDigit getCheckDigit() {
return this.checkDigit;
}
 
@Override
protected void encode() {
 
String intermediate;
final int length = this.content.length();
int i;
String evenString;
String oddString;
String addupString;
int spacer;
int addup;
int weight;
int checkDigit1;
int checkDigit2;
 
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in input");
}
 
intermediate = "21"; // Start
for (i = 0; i < length; i++) {
intermediate += MSI_PLESS_TABLE[Character.getNumericValue(this.content.charAt(i))];
}
 
this.readable = this.content;
 
if (this.checkDigit == CheckDigit.MOD10 || this.checkDigit == CheckDigit.MOD10_MOD10) {
/* Add Modulo-10 check digit */
evenString = "";
oddString = "";
 
spacer = this.content.length() & 1;
 
for (i = this.content.length() - 1; i >= 0; i--) {
if (spacer == 1) {
if ((i & 1) != 0) {
evenString = this.content.charAt(i) + evenString;
} else {
oddString = this.content.charAt(i) + oddString;
}
} else {
if ((i & 1) != 0) {
oddString = this.content.charAt(i) + oddString;
} else {
evenString = this.content.charAt(i) + evenString;
}
}
}
 
if (oddString.length() == 0) {
addupString = "0";
} else {
addupString = Integer.toString(Integer.parseInt(oddString) * 2);
}
 
addupString += evenString;
 
addup = 0;
for (i = 0; i < addupString.length(); i++) {
addup += addupString.charAt(i) - '0';
}
 
checkDigit1 = 10 - addup % 10;
if (checkDigit1 == 10) {
checkDigit1 = 0;
}
 
intermediate += MSI_PLESS_TABLE[checkDigit1];
this.readable += checkDigit1;
}
 
if (this.checkDigit == CheckDigit.MOD11 || this.checkDigit == CheckDigit.MOD11_MOD10) {
/* Add a Modulo-11 check digit */
weight = 2;
addup = 0;
for (i = this.content.length() - 1; i >= 0; i--) {
addup += (this.content.charAt(i) - '0') * weight;
weight++;
 
if (weight == 8) {
weight = 2;
}
}
 
checkDigit1 = 11 - addup % 11;
 
if (checkDigit1 == 11) {
checkDigit1 = 0;
}
 
this.readable += checkDigit1;
if (checkDigit1 == 10) {
intermediate += MSI_PLESS_TABLE[1];
intermediate += MSI_PLESS_TABLE[0];
} else {
intermediate += MSI_PLESS_TABLE[checkDigit1];
}
}
 
if (this.checkDigit == CheckDigit.MOD10_MOD10 || this.checkDigit == CheckDigit.MOD11_MOD10) {
/* Add a second Modulo-10 check digit */
evenString = "";
oddString = "";
 
spacer = this.readable.length() & 1;
 
for (i = this.readable.length() - 1; i >= 0; i--) {
if (spacer == 1) {
if ((i & 1) != 0) {
evenString = this.readable.charAt(i) + evenString;
} else {
oddString = this.readable.charAt(i) + oddString;
}
} else {
if ((i & 1) != 0) {
oddString = this.readable.charAt(i) + oddString;
} else {
evenString = this.readable.charAt(i) + evenString;
}
}
}
 
if (oddString.length() == 0) {
addupString = "0";
} else {
addupString = Integer.toString(Integer.parseInt(oddString) * 2);
}
 
addupString += evenString;
 
addup = 0;
for (i = 0; i < addupString.length(); i++) {
addup += addupString.charAt(i) - '0';
}
 
checkDigit2 = 10 - addup % 10;
if (checkDigit2 == 10) {
checkDigit2 = 0;
}
 
intermediate += MSI_PLESS_TABLE[checkDigit2];
this.readable += checkDigit2;
}
 
intermediate += "121"; // Stop
 
this.pattern = new String[] { intermediate };
this.row_count = 1;
this.row_height = new int[] { -1 };
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/TextBox.java
New file
0,0 → 1,62
/*
* Copyright 2014-2018 Robin Stuart, Daniel Gredler
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
 
package uk.org.okapibarcode.backend;
 
/**
* A simple text item class.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class TextBox {
 
/** The X position of the text's left boundary. */
public final double x;
 
/** The Y position of the text baseline. */
public final double y;
 
/** The width of the text box. */
public final double width;
 
/** The text value. */
public final String text;
 
/** The text alignment. */
public final HumanReadableAlignment alignment;
 
/**
* Creates a new instance.
*
* @param x the X position of the text's left boundary
* @param y the Y position of the text baseline
* @param width the width of the text box
* @param text the text value
* @param alignment the text alignment
*/
public TextBox(final double x, final double y, final double width, final String text, final HumanReadableAlignment alignment) {
this.x = x;
this.y = y;
this.width = width;
this.text = text;
this.alignment = alignment;
}
 
/** {@inheritDoc} */
@Override
public String toString() {
return "TextBox[x=" + this.x + ", y=" + this.y + ", width=" + this.width + ", text=" + this.text + ", alignment=" + this.alignment + "]";
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/DataMatrix.java
New file
0,0 → 1,1635
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
import java.util.Arrays;
 
/**
* <p>
* Implements Data Matrix ECC 200 bar code symbology According to ISO/IEC 16022:2006.
*
* <p>
* Data Matrix is a 2D matrix symbology capable of encoding characters in the ISO/IEC 8859-1
* (Latin-1) character set.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class DataMatrix extends Symbol {
 
public enum ForceMode {
NONE, SQUARE, RECTANGULAR
}
 
private enum Mode {
NULL, DM_ASCII, DM_C40, DM_TEXT, DM_X12, DM_EDIFACT, DM_BASE256
}
 
private static final int[] C40_SHIFT = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 };
 
private static final int[] C40_VALUE = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 22,
23, 24, 25, 26, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
 
private static final int[] TEXT_SHIFT = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3 };
 
private static final int[] TEXT_VALUE = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 22, 23, 24, 25,
26, 0, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 27, 28, 29, 30, 31 };
 
private static final int[] INT_SYMBOL = { 0, 1, 3, 5, 7, 8, 10, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 2, 4, 6, 9, 11, 14 };
 
private static final int[] MATRIX_H = { 10, 12, 8, 14, 8, 16, 12, 18, 20, 12, 22, 16, 24, 26, 16, 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144 };
 
private static final int[] MATRIX_W = { 10, 12, 18, 14, 32, 16, 26, 18, 20, 36, 22, 36, 24, 26, 48, 32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144 };
 
private static final int[] MATRIX_FH = { 10, 12, 8, 14, 8, 16, 12, 18, 20, 12, 22, 16, 24, 26, 16, 16, 18, 20, 22, 24, 26, 16, 18, 20, 22, 24, 26, 20, 22, 24 };
 
private static final int[] MATRIX_FW = { 10, 12, 18, 14, 16, 16, 26, 18, 20, 18, 22, 18, 24, 26, 24, 16, 18, 20, 22, 24, 26, 16, 18, 20, 22, 24, 26, 20, 22, 24 };
 
private static final int[] MATRIX_BYTES = { 3, 5, 5, 8, 10, 12, 16, 18, 22, 22, 30, 32, 36, 44, 49, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050, 1304, 1558 };
 
private static final int[] MATRIX_DATA_BLOCK = { 3, 5, 5, 8, 10, 12, 16, 18, 22, 22, 30, 32, 36, 44, 49, 62, 86, 114, 144, 174, 102, 140, 92, 114, 144, 174, 136, 175, 163, 156 };
 
private static final int[] MATRIX_RS_BLOCK = { 5, 7, 7, 10, 11, 12, 14, 14, 18, 18, 20, 24, 24, 28, 28, 36, 42, 48, 56, 68, 42, 56, 36, 48, 56, 68, 56, 68, 62, 62 };
 
private static final int DM_SIZES_COUNT = MATRIX_H.length;
 
// user-specified values and settings
 
private ForceMode forceMode = ForceMode.NONE;
private int preferredSize;
private int structuredAppendFileId = 1;
private int structuredAppendPosition = 1;
private int structuredAppendTotal = 1;
 
// internal state calculated when setContent() is called
 
private int actualSize = -1;
private final int[] target = new int[2200];
private final int[] binary = new int[2200];
private int binary_length;
private Mode last_mode;
private int[] places;
private int process_p;
private final int[] process_buffer = new int[8];
private int codewordCount;
 
/**
* Forces the symbol to be either square or rectangular (non-square).
*
* @param forceMode the force mode to use
*/
public void setForceMode(final ForceMode forceMode) {
this.forceMode = forceMode;
}
 
/**
* Returns the force mode used by this symbol.
*
* @return the force mode used by this symbol
*/
public ForceMode getForceMode() {
return this.forceMode;
}
 
/**
* Sets the preferred symbol size according to the values in the following table. Values may be
* ignored if the data is too big to fit in the specified symbol, or if
* {@link #setForceMode(ForceMode)} has been invoked.
*
* <table summary="Available Data Matrix symbol sizes">
* <tbody>
* <tr>
* <th>Input</th>
* <th>Symbol Size</th>
* <th>Input</th>
* <th>Symbol Size</th>
* </tr>
* <tr>
* <td>1</td>
* <td>10 x 10</td>
* <td>16</td>
* <td>64 x 64</td>
* </tr>
* <tr>
* <td>2</td>
* <td>12 x 12</td>
* <td>17</td>
* <td>72 x 72</td>
* </tr>
* <tr>
* <td>3</td>
* <td>14 x 14</td>
* <td>18</td>
* <td>80 x 80</td>
* </tr>
* <tr>
* <td>4</td>
* <td>16 x 16</td>
* <td>19</td>
* <td>88 x 88</td>
* </tr>
* <tr>
* <td>5</td>
* <td>18 x 18</td>
* <td>20</td>
* <td>96 x 96</td>
* </tr>
* <tr>
* <td>6</td>
* <td>20 x 20</td>
* <td>21</td>
* <td>104 x 104</td>
* </tr>
* <tr>
* <td>7</td>
* <td>22 x 22</td>
* <td>22</td>
* <td>120 x 120</td>
* </tr>
* <tr>
* <td>8</td>
* <td>24 x 24</td>
* <td>23</td>
* <td>132 x 132</td>
* </tr>
* <tr>
* <td>9</td>
* <td>26 x 26</td>
* <td>24</td>
* <td>144 x 144</td>
* </tr>
* <tr>
* <td>10</td>
* <td>32 x 32</td>
* <td>25</td>
* <td>8 x 18</td>
* </tr>
* <tr>
* <td>11</td>
* <td>36 x 36</td>
* <td>26</td>
* <td>8 x 32</td>
* </tr>
* <tr>
* <td>12</td>
* <td>40 x 40</td>
* <td>27</td>
* <td>12 x 26</td>
* </tr>
* <tr>
* <td>13</td>
* <td>44 x 44</td>
* <td>28</td>
* <td>12 x 36</td>
* </tr>
* <tr>
* <td>14</td>
* <td>48 x 48</td>
* <td>29</td>
* <td>16 x 36</td>
* </tr>
* <tr>
* <td>15</td>
* <td>52 x 52</td>
* <td>30</td>
* <td>16 x 48</td>
* </tr>
* </tbody>
* </table>
*
* @param size the symbol size to use (1 - 30 inclusive)
*/
public void setPreferredSize(final int size) {
this.preferredSize = size;
}
 
/**
* Returns the preferred symbol size.
*
* @return the preferred symbol size
* @see #setPreferredSize(int)
*/
public int getPreferredSize() {
return this.preferredSize;
}
 
/**
* Returns the actual symbol size used. Available after the symbol is encoded.
*
* @return the actual symbol size used
*/
public int getActualSize() {
if (this.actualSize != -1) {
return this.actualSize;
} else {
throw new IllegalStateException("Actual size not calculated until symbol is encoded.");
}
}
 
/**
* Returns the actual width (columns) used for the symbol. Available after the symbol is
* encoded.
*
* @return the actual width (columns) used for the symbol
*/
public int getActualWidth() {
final int index1 = getActualSize() - 1;
final int index2 = INT_SYMBOL[index1];
return MATRIX_W[index2];
}
 
/**
* Returns the actual height (rows) used for the symbol. Available after the symbol is encoded.
*
* @return the actual height (rows) used for the symbol
*/
public int getActualHeight() {
final int index1 = getActualSize() - 1;
final int index2 = INT_SYMBOL[index1];
return MATRIX_H[index2];
}
 
/**
* If this Data Matrix symbol is part of a series of Data Matrix symbols appended in a
* structured format, this method sets the position of this symbol in the series. Valid values
* are 1 through 16 inclusive.
*
* @param position the position of this Data Matrix symbol in the structured append series
*/
public void setStructuredAppendPosition(final int position) {
if (position < 1 || position > 16) {
throw new IllegalArgumentException("Invalid Data Matrix structured append position: " + position);
}
this.structuredAppendPosition = position;
}
 
/**
* Returns the position of this Data Matrix symbol in a series of symbols using structured
* append. If this symbol is not part of such a series, this method will return <code>1</code>.
*
* @return the position of this Data Matrix symbol in a series of symbols using structured
* append
*/
public int getStructuredAppendPosition() {
return this.structuredAppendPosition;
}
 
/**
* If this Data Matrix symbol is part of a series of Data Matrix symbols appended in a
* structured format, this method sets the total number of symbols in the series. Valid values
* are 1 through 16 inclusive. A value of 1 indicates that this symbol is not part of a
* structured append series.
*
* @param total the total number of Data Matrix symbols in the structured append series
*/
public void setStructuredAppendTotal(final int total) {
if (total < 1 || total > 16) {
throw new IllegalArgumentException("Invalid Data Matrix structured append total: " + total);
}
this.structuredAppendTotal = total;
}
 
/**
* Returns the size of the series of Data Matrix symbols using structured append that this
* symbol is part of. If this symbol is not part of a structured append series, this method will
* return <code>1</code>.
*
* @return size of the series that this symbol is part of
*/
public int getStructuredAppendTotal() {
return this.structuredAppendTotal;
}
 
/**
* If this Data Matrix symbol is part of a series of Data Matrix symbols appended in a
* structured format, this method sets the unique file ID for the series. Valid values are 1
* through 64,516 inclusive.
*
* @param fileId the unique file ID for the series that this symbol is part of
*/
public void setStructuredAppendFileId(final int fileId) {
if (fileId < 1 || fileId > 64_516) {
throw new IllegalArgumentException("Invalid Data Matrix structured append file ID: " + fileId);
}
this.structuredAppendFileId = fileId;
}
 
/**
* Returns the unique file ID of the series of Data Matrix symbols using structured append that
* this symbol is part of. If this symbol is not part of a structured append series, this method
* will return <code>1</code>.
*
* @return the unique file ID for the series that this symbol is part of
*/
public int getStructuredAppendFileId() {
return this.structuredAppendFileId;
}
 
@Override
protected boolean gs1Supported() {
return true;
}
 
@Override
protected void encode() {
 
int i, binlen, skew = 0;
int symbolsize, optionsize, calcsize;
int taillength;
int H, W, FH, FW, datablock, bytes, rsblock;
int x, y, NC, NR, v;
int[] grid;
final StringBuilder bin = new StringBuilder();
 
eciProcess(); // Get ECI mode
 
binlen = generateCodewords();
 
if (this.preferredSize >= 1 && this.preferredSize <= DM_SIZES_COUNT) {
optionsize = INT_SYMBOL[this.preferredSize - 1];
} else {
optionsize = -1;
}
 
calcsize = DM_SIZES_COUNT - 1;
for (i = DM_SIZES_COUNT - 1; i > -1; i--) {
if (MATRIX_BYTES[i] >= binlen + this.process_p) {
calcsize = i;
}
}
 
if (optionsize == -1) {
// We are in automatic size mode as the exact symbol size was not given
// Now check the detailed search options square only or rectangular only
if (this.forceMode == ForceMode.SQUARE) {
/* Skip rectangular symbols in square only mode */
while (calcsize < DM_SIZES_COUNT && MATRIX_H[calcsize] != MATRIX_W[calcsize]) {
calcsize++;
}
} else if (this.forceMode == ForceMode.RECTANGULAR) {
/* Skip square symbols in rectangular only mode */
while (calcsize < DM_SIZES_COUNT && MATRIX_H[calcsize] == MATRIX_W[calcsize]) {
calcsize++;
}
}
if (calcsize >= DM_SIZES_COUNT) {
throw new OkapiException("Data too long to fit in symbol");
}
symbolsize = calcsize;
} else {
// The symbol size was specified by the user
// Thus check if the data fits into this symbol size and use this size
if (calcsize > optionsize) {
throw new OkapiException("Input too long for selected symbol size");
}
symbolsize = optionsize;
}
 
// Now we know the symbol size we can handle the remaining data in the process buffer.
final int symbolsLeft = MATRIX_BYTES[symbolsize] - binlen;
binlen = encodeRemainder(symbolsLeft, binlen);
 
if (binlen > MATRIX_BYTES[symbolsize]) {
throw new OkapiException("Data too long to fit in symbol");
}
 
H = MATRIX_H[symbolsize];
W = MATRIX_W[symbolsize];
FH = MATRIX_FH[symbolsize];
FW = MATRIX_FW[symbolsize];
bytes = MATRIX_BYTES[symbolsize];
datablock = MATRIX_DATA_BLOCK[symbolsize];
rsblock = MATRIX_RS_BLOCK[symbolsize];
 
this.codewordCount = datablock + rsblock; // data codewords + error correction codewords
 
taillength = bytes - binlen;
 
if (taillength != 0) {
addPadBits(binlen, taillength);
}
 
// ecc code
if (symbolsize == 29) {
skew = 1;
}
calculateErrorCorrection(bytes, datablock, rsblock, skew);
NC = W - 2 * (W / FW);
NR = H - 2 * (H / FH);
this.places = new int[NC * NR];
placeData(NR, NC);
grid = new int[W * H];
for (i = 0; i < W * H; i++) {
grid[i] = 0;
}
for (y = 0; y < H; y += FH) {
for (x = 0; x < W; x++) {
grid[y * W + x] = 1;
}
for (x = 0; x < W; x += 2) {
grid[(y + FH - 1) * W + x] = 1;
}
}
for (x = 0; x < W; x += FW) {
for (y = 0; y < H; y++) {
grid[y * W + x] = 1;
}
for (y = 0; y < H; y += 2) {
grid[y * W + x + FW - 1] = 1;
}
}
for (y = 0; y < NR; y++) {
for (x = 0; x < NC; x++) {
v = this.places[(NR - y - 1) * NC + x];
if (v == 1 || v > 7 && (this.target[(v >> 3) - 1] & 1 << (v & 7)) != 0) {
grid[(1 + y + 2 * (y / (FH - 2))) * W + 1 + x + 2 * (x / (FW - 2))] = 1;
}
}
}
 
this.actualSize = positionOf(symbolsize, INT_SYMBOL) + 1;
this.readable = "";
this.pattern = new String[H];
this.row_count = H;
this.row_height = new int[H];
for (y = H - 1; y >= 0; y--) {
bin.setLength(0);
for (x = 0; x < W; x++) {
if (grid[W * y + x] == 1) {
bin.append('1');
} else {
bin.append('0');
}
}
this.pattern[H - y - 1] = bin2pat(bin);
this.row_height[H - y - 1] = this.moduleWidth;
}
 
infoLine("Grid Size: " + W + " X " + H);
infoLine("Data Codewords: " + datablock);
infoLine("ECC Codewords: " + rsblock);
}
 
@Override
protected int[] getCodewords() {
return Arrays.copyOf(this.target, this.codewordCount);
}
 
private int generateCodewords() {
/* Encodes data using ASCII, C40, Text, X12, EDIFACT or Base 256 modes as appropriate */
/* Supports encoding FNC1 in supporting systems */
/* Supports ECI encoding for whole message only, not inline switching */
 
info("Encoding: ");
int sp, tp, i;
Mode current_mode, next_mode;
int inputlen = this.inputData.length;
 
sp = 0;
tp = 0;
this.process_p = 0;
 
for (i = 0; i < 8; i++) {
this.process_buffer[i] = 0;
}
this.binary_length = 0;
 
/* step (a) */
current_mode = Mode.DM_ASCII;
next_mode = Mode.DM_ASCII;
 
if (this.structuredAppendTotal != 1) {
 
/* FNC2 */
this.target[tp] = 233;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("FNC2 ");
 
/* symbol sequence indicator (position + total) */
final int ssi = this.structuredAppendPosition - 1 << 4 | 17 - this.structuredAppendTotal;
this.target[tp] = ssi;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
infoSpace(ssi);
 
/* file identification codeword 1 (valid values 1 - 254) */
final int id1 = 1 + (this.structuredAppendFileId - 1) / 254;
this.target[tp] = id1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
infoSpace(id1);
 
/* file identification codeword 2 (valid values 1 - 254) */
final int id2 = 1 + (this.structuredAppendFileId - 1) % 254;
this.target[tp] = id2;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
infoSpace(id2);
}
 
if (this.inputDataType == DataType.GS1) {
this.target[tp] = 232;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("FNC1 ");
} /* FNC1 */
 
if (this.readerInit) {
if (this.inputDataType == DataType.GS1) {
throw new OkapiException("Cannot encode in GS1 mode and Reader Initialisation at the same time");
} else {
this.target[tp] = 234; /* FNC3 */
tp++; /* Reader Programming */
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("RP ");
}
}
 
if (this.eciMode != 3) {
this.target[tp] = 241; // ECI
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
if (this.eciMode <= 126) {
this.target[tp] = this.eciMode + 1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
}
if (this.eciMode >= 127 && this.eciMode <= 16382) {
this.target[tp] = (this.eciMode - 127) / 254 + 128;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.target[tp] = (this.eciMode - 127) % 254 + 1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
}
if (this.eciMode >= 16383) {
this.target[tp] = (this.eciMode - 16383) / 64516 + 192;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.target[tp] = (this.eciMode - 16383) / 254 % 254 + 1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.target[tp] = (this.eciMode - 16383) % 254 + 1;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
}
info("ECI " + this.eciMode + " ");
}
 
/* Check for Macro05/Macro06 */
/* "[)>[RS]05[GS]...[RS][EOT]" -> CW 236 */
/* "[)>[RS]06[GS]...[RS][EOT]" -> CW 237 */
if (tp == 0 & sp == 0 && inputlen >= 9) {
if (this.inputData[0] == '[' && this.inputData[1] == ')' && this.inputData[2] == '>' && this.inputData[3] == '\u001e' && this.inputData[4] == '0'
&& (this.inputData[5] == '5' || this.inputData[5] == '6') && this.inputData[6] == '\u001d' && this.inputData[inputlen - 2] == '\u001e'
&& this.inputData[inputlen - 1] == '\u0004') {
/* Output macro codeword */
if (this.inputData[5] == '5') {
this.target[tp] = 236;
info("Macro05 ");
} else {
this.target[tp] = 237;
info("Macro06 ");
}
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
/* Remove macro characters from input string */
sp = 7;
inputlen -= 2;
this.inputData = Arrays.copyOf(this.inputData, this.inputData.length - 2);
}
}
 
while (sp < inputlen) {
 
current_mode = next_mode;
 
/* step (b) - ASCII encodation */
if (current_mode == Mode.DM_ASCII) {
next_mode = Mode.DM_ASCII;
 
for (i = 0; i < 8; i++) {
this.process_buffer[i] = 0;
}
 
if (isTwoDigits(sp)) {
this.target[tp] = 10 * Character.getNumericValue(this.inputData[sp]) + Character.getNumericValue(this.inputData[sp + 1]) + 130;
infoSpace(this.target[tp] - 130);
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
sp += 2;
} else {
next_mode = lookAheadTest(sp, current_mode);
 
if (next_mode != Mode.DM_ASCII) {
switch (next_mode) {
case DM_C40:
this.target[tp] = 230;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("C40 ");
break;
case DM_TEXT:
this.target[tp] = 239;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("TEX ");
break;
case DM_X12:
this.target[tp] = 238;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("X12 ");
break;
case DM_EDIFACT:
this.target[tp] = 240;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("EDI ");
break;
case DM_BASE256:
this.target[tp] = 231;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("BAS ");
break;
}
} else {
if (this.inputData[sp] > 127) {
this.target[tp] = 235; /* FNC4 */
 
info("FNC4 ");
tp++;
this.target[tp] = this.inputData[sp] - 128 + 1;
infoSpace(this.target[tp] - 1);
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
} else {
if (this.inputData[sp] == FNC1) {
this.target[tp] = 232; /* FNC1 */
info("FNC1 ");
} else {
this.target[tp] = this.inputData[sp] + 1;
infoSpace(this.target[tp] - 1);
}
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
}
sp++;
}
}
}
 
/* step (c) C40 encodation */
if (current_mode == Mode.DM_C40) {
int shift_set, value;
 
next_mode = Mode.DM_C40;
if (this.process_p == 0) {
next_mode = lookAheadTest(sp, current_mode);
}
 
if (next_mode != Mode.DM_C40) {
this.target[tp] = 254;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++; /* Unlatch */
 
next_mode = Mode.DM_ASCII;
info("ASC ");
} else {
if (this.inputData[sp] == FNC1) {
shift_set = 2;
value = 27; /* FNC1 */
} else if (this.inputData[sp] > 127) {
this.process_buffer[this.process_p] = 1;
this.process_p++;
this.process_buffer[this.process_p] = 30;
this.process_p++; /* Upper Shift */
 
shift_set = C40_SHIFT[this.inputData[sp] - 128];
value = C40_VALUE[this.inputData[sp] - 128];
} else {
shift_set = C40_SHIFT[this.inputData[sp]];
value = C40_VALUE[this.inputData[sp]];
}
 
if (shift_set != 0) {
this.process_buffer[this.process_p] = shift_set - 1;
this.process_p++;
}
this.process_buffer[this.process_p] = value;
this.process_p++;
 
while (this.process_p >= 3) {
int iv;
 
iv = 1600 * this.process_buffer[0] + 40 * this.process_buffer[1] + this.process_buffer[2] + 1;
this.target[tp] = iv / 256;
tp++;
this.target[tp] = iv % 256;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("(" + this.process_buffer[0] + " " + this.process_buffer[1] + " " + this.process_buffer[2] + ") ");
 
this.process_buffer[0] = this.process_buffer[3];
this.process_buffer[1] = this.process_buffer[4];
this.process_buffer[2] = this.process_buffer[5];
this.process_buffer[3] = 0;
this.process_buffer[4] = 0;
this.process_buffer[5] = 0;
this.process_p -= 3;
}
sp++;
}
}
 
/* step (d) Text encodation */
if (current_mode == Mode.DM_TEXT) {
int shift_set, value;
 
next_mode = Mode.DM_TEXT;
if (this.process_p == 0) {
next_mode = lookAheadTest(sp, current_mode);
}
 
if (next_mode != Mode.DM_TEXT) {
this.target[tp] = 254;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++; /* Unlatch */
 
next_mode = Mode.DM_ASCII;
info("ASC ");
} else {
if (this.inputData[sp] == FNC1) {
shift_set = 2;
value = 27; /* FNC1 */
} else if (this.inputData[sp] > 127) {
this.process_buffer[this.process_p] = 1;
this.process_p++;
this.process_buffer[this.process_p] = 30;
this.process_p++; /* Upper Shift */
 
shift_set = TEXT_SHIFT[this.inputData[sp] - 128];
value = TEXT_VALUE[this.inputData[sp] - 128];
} else {
shift_set = TEXT_SHIFT[this.inputData[sp]];
value = TEXT_VALUE[this.inputData[sp]];
}
 
if (shift_set != 0) {
this.process_buffer[this.process_p] = shift_set - 1;
this.process_p++;
}
this.process_buffer[this.process_p] = value;
this.process_p++;
 
while (this.process_p >= 3) {
int iv;
 
iv = 1600 * this.process_buffer[0] + 40 * this.process_buffer[1] + this.process_buffer[2] + 1;
this.target[tp] = iv / 256;
tp++;
this.target[tp] = iv % 256;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("(" + this.process_buffer[0] + " " + this.process_buffer[1] + " " + this.process_buffer[2] + ") ");
 
this.process_buffer[0] = this.process_buffer[3];
this.process_buffer[1] = this.process_buffer[4];
this.process_buffer[2] = this.process_buffer[5];
this.process_buffer[3] = 0;
this.process_buffer[4] = 0;
this.process_buffer[5] = 0;
this.process_p -= 3;
}
sp++;
}
}
 
/* step (e) X12 encodation */
if (current_mode == Mode.DM_X12) {
int value = 0;
 
next_mode = Mode.DM_X12;
if (this.process_p == 0) {
next_mode = lookAheadTest(sp, current_mode);
}
 
if (next_mode != Mode.DM_X12) {
this.target[tp] = 254;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++; /* Unlatch */
 
next_mode = Mode.DM_ASCII;
info("ASC ");
} else {
if (this.inputData[sp] == 13) {
value = 0;
}
if (this.inputData[sp] == '*') {
value = 1;
}
if (this.inputData[sp] == '>') {
value = 2;
}
if (this.inputData[sp] == ' ') {
value = 3;
}
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') {
value = this.inputData[sp] - '0' + 4;
}
if (this.inputData[sp] >= 'A' && this.inputData[sp] <= 'Z') {
value = this.inputData[sp] - 'A' + 14;
}
 
this.process_buffer[this.process_p] = value;
this.process_p++;
 
while (this.process_p >= 3) {
int iv;
 
iv = 1600 * this.process_buffer[0] + 40 * this.process_buffer[1] + this.process_buffer[2] + 1;
this.target[tp] = iv / 256;
tp++;
this.target[tp] = iv % 256;
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("(" + this.process_buffer[0] + " " + this.process_buffer[1] + " " + this.process_buffer[2] + ") ");
 
this.process_buffer[0] = this.process_buffer[3];
this.process_buffer[1] = this.process_buffer[4];
this.process_buffer[2] = this.process_buffer[5];
this.process_buffer[3] = 0;
this.process_buffer[4] = 0;
this.process_buffer[5] = 0;
this.process_p -= 3;
}
sp++;
}
}
 
/* step (f) EDIFACT encodation */
if (current_mode == Mode.DM_EDIFACT) {
int value = 0;
 
next_mode = Mode.DM_EDIFACT;
if (this.process_p == 3) {
next_mode = lookAheadTest(sp, current_mode);
}
 
if (next_mode != Mode.DM_EDIFACT) {
this.process_buffer[this.process_p] = 31;
this.process_p++;
next_mode = Mode.DM_ASCII;
} else {
if (this.inputData[sp] >= '@' && this.inputData[sp] <= '^') {
value = this.inputData[sp] - '@';
}
if (this.inputData[sp] >= ' ' && this.inputData[sp] <= '?') {
value = this.inputData[sp];
}
 
this.process_buffer[this.process_p] = value;
this.process_p++;
sp++;
}
 
while (this.process_p >= 4) {
this.target[tp] = (this.process_buffer[0] << 2) + ((this.process_buffer[1] & 0x30) >> 4);
tp++;
this.target[tp] = ((this.process_buffer[1] & 0x0f) << 4) + ((this.process_buffer[2] & 0x3c) >> 2);
tp++;
this.target[tp] = ((this.process_buffer[2] & 0x03) << 6) + this.process_buffer[3];
tp++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
this.binary[this.binary_length] = ' ';
this.binary_length++;
info("(" + this.process_buffer[0] + " " + this.process_buffer[1] + " " + this.process_buffer[2] + ") ");
 
this.process_buffer[0] = this.process_buffer[4];
this.process_buffer[1] = this.process_buffer[5];
this.process_buffer[2] = this.process_buffer[6];
this.process_buffer[3] = this.process_buffer[7];
this.process_buffer[4] = 0;
this.process_buffer[5] = 0;
this.process_buffer[6] = 0;
this.process_buffer[7] = 0;
this.process_p -= 4;
}
}
 
/* step (g) Base 256 encodation */
if (current_mode == Mode.DM_BASE256) {
next_mode = lookAheadTest(sp, current_mode);
 
if (next_mode == Mode.DM_BASE256) {
this.target[tp] = this.inputData[sp];
infoSpace(this.target[tp]);
tp++;
sp++;
this.binary[this.binary_length] = 'b';
this.binary_length++;
} else {
next_mode = Mode.DM_ASCII;
info("ASC ");
}
}
 
if (tp > 1558) {
throw new OkapiException("Data too long to fit in symbol");
}
 
} /* while */
 
/* Add length and randomising algorithm to b256 */
i = 0;
while (i < tp) {
if (this.binary[i] == 'b') {
if (i == 0 || this.binary[i - 1] != 'b') {
/* start of binary data */
int binary_count; /* length of b256 data */
 
for (binary_count = 0; binary_count + i < tp && this.binary[binary_count + i] == 'b'; binary_count++) {
;
}
 
if (binary_count <= 249) {
insertAt(i, 'b');
insertValueAt(i, tp, (char) binary_count);
tp++;
} else {
insertAt(i, 'b');
insertAt(i + 1, 'b');
insertValueAt(i, tp, (char) (binary_count / 250 + 249));
tp++;
insertValueAt(i + 1, tp, (char) (binary_count % 250));
tp++;
}
}
}
i++;
}
 
for (i = 0; i < tp; i++) {
if (this.binary[i] == 'b') {
int prn, temp;
 
prn = 149 * (i + 1) % 255 + 1;
temp = this.target[i] + prn;
if (temp <= 255) {
this.target[i] = temp;
} else {
this.target[i] = temp - 256;
}
}
}
 
infoLine();
info("Codewords: ");
for (i = 0; i < tp; i++) {
infoSpace(this.target[i]);
}
infoLine();
 
this.last_mode = current_mode;
return tp;
}
 
private int encodeRemainder(final int symbols_left, int target_length) {
 
final int inputlen = this.inputData.length;
 
switch (this.last_mode) {
case DM_C40:
case DM_TEXT:
if (this.process_p == 1) // 1 data character left to encode.
{
if (symbols_left > 1) {
this.target[target_length] = 254;
target_length++; // Unlatch and encode remaining data in ascii.
}
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
} else if (this.process_p == 2) // 2 data characters left to encode.
{
// Pad with shift 1 value (0) and encode as double.
final int intValue = 1600 * this.process_buffer[0] + 40 * this.process_buffer[1] + 1; // ie
// (0
// +
// 1).
this.target[target_length] = intValue / 256;
target_length++;
this.target[target_length] = intValue % 256;
target_length++;
if (symbols_left > 2) {
this.target[target_length] = 254; // Unlatch
target_length++;
}
} else {
if (symbols_left > 0) {
this.target[target_length] = 254; // Unlatch
target_length++;
}
}
break;
 
case DM_X12:
if (symbols_left == this.process_p && this.process_p == 1) {
// Unlatch not required!
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
} else {
this.target[target_length] = 254;
target_length++; // Unlatch.
 
if (this.process_p == 1) {
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
}
 
if (this.process_p == 2) {
this.target[target_length] = this.inputData[inputlen - 2] + 1;
target_length++;
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
}
}
break;
 
case DM_EDIFACT:
if (symbols_left <= 2) // Unlatch not required!
{
if (this.process_p == 1) {
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
}
 
if (this.process_p == 2) {
this.target[target_length] = this.inputData[inputlen - 2] + 1;
target_length++;
this.target[target_length] = this.inputData[inputlen - 1] + 1;
target_length++;
}
} else {
// Append edifact unlatch value (31) and empty buffer
if (this.process_p == 0) {
this.target[target_length] = 31 << 2;
target_length++;
}
 
if (this.process_p == 1) {
this.target[target_length] = (this.process_buffer[0] << 2) + ((31 & 0x30) >> 4);
target_length++;
this.target[target_length] = (31 & 0x0f) << 4;
target_length++;
}
 
if (this.process_p == 2) {
this.target[target_length] = (this.process_buffer[0] << 2) + ((this.process_buffer[1] & 0x30) >> 4);
target_length++;
this.target[target_length] = ((this.process_buffer[1] & 0x0f) << 4) + ((31 & 0x3c) >> 2);
target_length++;
this.target[target_length] = (31 & 0x03) << 6;
target_length++;
}
 
if (this.process_p == 3) {
this.target[target_length] = (this.process_buffer[0] << 2) + ((this.process_buffer[1] & 0x30) >> 4);
target_length++;
this.target[target_length] = ((this.process_buffer[1] & 0x0f) << 4) + ((this.process_buffer[2] & 0x3c) >> 2);
target_length++;
this.target[target_length] = ((this.process_buffer[2] & 0x03) << 6) + 31;
target_length++;
}
}
break;
}
 
return target_length;
}
 
private boolean isTwoDigits(final int pos) {
return pos + 1 < this.inputData.length && Character.isDigit((char) this.inputData[pos]) && Character.isDigit((char) this.inputData[pos + 1]);
}
 
private Mode lookAheadTest(final int position, final Mode current_mode) {
 
/* 'look ahead test' from Annex P */
 
double ascii_count, c40_count, text_count, x12_count, edf_count, b256_count, best_count;
int sp;
final int sourcelen = this.inputData.length;
Mode best_scheme = Mode.NULL;
final double stiction = 1.0F / 24.0F; // smallest change to act on, to get around floating
// point inaccuracies
 
/* step (j) */
if (current_mode == Mode.DM_ASCII) {
ascii_count = 0.0;
c40_count = 1.0;
text_count = 1.0;
x12_count = 1.0;
edf_count = 1.0;
b256_count = 1.25;
} else {
ascii_count = 1.0;
c40_count = 2.0;
text_count = 2.0;
x12_count = 2.0;
edf_count = 2.0;
b256_count = 2.25;
}
 
switch (current_mode) {
case DM_C40: // (j)(2)
c40_count = 0.0;
break;
case DM_TEXT: // (j)(3)
text_count = 0.0;
break;
case DM_X12: // (j)(4)
x12_count = 0.0;
break;
case DM_EDIFACT: // (j)(5)
edf_count = 0.0;
break;
case DM_BASE256: // (j)(6)
b256_count = 0.0;
break;
}
 
sp = position;
 
do {
if (sp == sourcelen) {
/* At the end of data ... step (k) */
ascii_count = Math.ceil(ascii_count);
b256_count = Math.ceil(b256_count);
edf_count = Math.ceil(edf_count);
text_count = Math.ceil(text_count);
x12_count = Math.ceil(x12_count);
c40_count = Math.ceil(c40_count);
 
best_count = c40_count;
best_scheme = Mode.DM_C40; // (k)(7)
 
if (x12_count < best_count - stiction) {
best_count = x12_count;
best_scheme = Mode.DM_X12; // (k)(6)
}
 
if (text_count < best_count - stiction) {
best_count = text_count;
best_scheme = Mode.DM_TEXT; // (k)(5)
}
 
if (edf_count < best_count - stiction) {
best_count = edf_count;
best_scheme = Mode.DM_EDIFACT; // (k)(4)
}
 
if (b256_count < best_count - stiction) {
best_count = b256_count;
best_scheme = Mode.DM_BASE256; // (k)(3)
}
 
if (ascii_count <= best_count + stiction) {
best_scheme = Mode.DM_ASCII; // (k)(2)
}
} else {
 
/* ascii ... step (l) */
if (this.inputData[sp] >= '0' && this.inputData[sp] <= '9') {
ascii_count += 0.5; // (l)(1)
} else {
if (this.inputData[sp] > 127) {
ascii_count = Math.ceil(ascii_count) + 2.0; // (l)(2)
} else {
ascii_count = Math.ceil(ascii_count) + 1.0; // (l)(3)
}
}
 
/* c40 ... step (m) */
if (this.inputData[sp] == ' ' || this.inputData[sp] >= '0' && this.inputData[sp] <= '9' || this.inputData[sp] >= 'A' && this.inputData[sp] <= 'Z') {
c40_count += 2.0 / 3.0; // (m)(1)
} else {
if (this.inputData[sp] > 127) {
c40_count += 8.0 / 3.0; // (m)(2)
} else {
c40_count += 4.0 / 3.0; // (m)(3)
}
}
 
/* text ... step (n) */
if (this.inputData[sp] == ' ' || this.inputData[sp] >= '0' && this.inputData[sp] <= '9' || this.inputData[sp] >= 'a' && this.inputData[sp] <= 'z') {
text_count += 2.0 / 3.0; // (n)(1)
} else {
if (this.inputData[sp] > 127) {
text_count += 8.0 / 3.0; // (n)(2)
} else {
text_count += 4.0 / 3.0; // (n)(3)
}
}
 
/* x12 ... step (o) */
if (isX12(this.inputData[sp])) {
x12_count += 2.0 / 3.0; // (o)(1)
} else {
if (this.inputData[sp] > 127) {
x12_count += 13.0 / 3.0; // (o)(2)
} else {
x12_count += 10.0 / 3.0; // (o)(3)
}
}
 
/* edifact ... step (p) */
if (this.inputData[sp] >= ' ' && this.inputData[sp] <= '^') {
edf_count += 3.0 / 4.0; // (p)(1)
} else {
if (this.inputData[sp] > 127) {
edf_count += 17.0; // (p)(2) > Value changed from ISO
} else {
edf_count += 13.0; // (p)(3) > Value changed from ISO
}
}
if (this.inputData[sp] == FNC1) {
edf_count += 13.0; // > Value changed from ISO
}
 
/* base 256 ... step (q) */
if (this.inputData[sp] == FNC1) {
b256_count += 4.0; // (q)(1)
} else {
b256_count += 1.0; // (q)(2)
}
}
 
if (sp > position + 3) {
/* 4 data characters processed ... step (r) */
 
/* step (r)(6) */
if (c40_count + 1.0 < ascii_count - stiction && c40_count + 1.0 < b256_count - stiction && c40_count + 1.0 < edf_count - stiction && c40_count + 1.0 < text_count - stiction) {
 
if (c40_count < x12_count - stiction) {
best_scheme = Mode.DM_C40;
}
 
if (c40_count >= x12_count - stiction && c40_count <= x12_count + stiction) {
if (p_r_6_2_1(sp, sourcelen)) {
// Test (r)(6)(ii)(i)
best_scheme = Mode.DM_X12;
} else {
best_scheme = Mode.DM_C40;
}
}
}
 
/* step (r)(5) */
if (x12_count + 1.0 < ascii_count - stiction && x12_count + 1.0 < b256_count - stiction && x12_count + 1.0 < edf_count - stiction && x12_count + 1.0 < text_count - stiction
&& x12_count + 1.0 < c40_count - stiction) {
best_scheme = Mode.DM_X12;
}
 
/* step (r)(4) */
if (text_count + 1.0 < ascii_count - stiction && text_count + 1.0 < b256_count - stiction && text_count + 1.0 < edf_count - stiction && text_count + 1.0 < x12_count - stiction
&& text_count + 1.0 < c40_count - stiction) {
best_scheme = Mode.DM_TEXT;
}
 
/* step (r)(3) */
if (edf_count + 1.0 < ascii_count - stiction && edf_count + 1.0 < b256_count - stiction && edf_count + 1.0 < text_count - stiction && edf_count + 1.0 < x12_count - stiction
&& edf_count + 1.0 < c40_count - stiction) {
best_scheme = Mode.DM_EDIFACT;
}
 
/* step (r)(2) */
if (b256_count + 1.0 <= ascii_count + stiction
|| b256_count + 1.0 < edf_count - stiction && b256_count + 1.0 < text_count - stiction && b256_count + 1.0 < x12_count - stiction && b256_count + 1.0 < c40_count - stiction) {
best_scheme = Mode.DM_BASE256;
}
 
/* step (r)(1) */
if (ascii_count + 1.0 <= b256_count + stiction && ascii_count + 1.0 <= edf_count + stiction && ascii_count + 1.0 <= text_count + stiction && ascii_count + 1.0 <= x12_count + stiction
&& ascii_count + 1.0 <= c40_count + stiction) {
best_scheme = Mode.DM_ASCII;
}
}
 
sp++;
 
} while (best_scheme == Mode.NULL); // step (s)
 
return best_scheme;
}
 
private boolean p_r_6_2_1(final int position, final int sourcelen) {
/*
* Annex P section (r)(6)(ii)(I) "If one of the three X12 terminator/separator characters
* first occurs in the yet to be processed data before a non-X12 character..."
*/
 
int i;
int nonX12Position = 0;
int specialX12Position = 0;
boolean retval = false;
 
for (i = position; i < sourcelen; i++) {
if (nonX12Position == 0 && !isX12(this.inputData[i])) {
nonX12Position = i;
}
 
if (specialX12Position == 0) {
if (this.inputData[i] == (char) 13 || this.inputData[i] == '*' || this.inputData[i] == '>') {
specialX12Position = i;
}
}
}
 
if (nonX12Position != 0 && specialX12Position != 0) {
if (specialX12Position < nonX12Position) {
retval = true;
}
}
 
return retval;
}
 
private boolean isX12(final int source) {
if (source == 13) {
return true;
}
if (source == 42) {
return true;
}
if (source == 62) {
return true;
}
if (source == 32) {
return true;
}
if (source >= '0' && source <= '9') {
return true;
}
if (source >= 'A' && source <= 'Z') {
return true;
}
 
return false;
}
 
private void calculateErrorCorrection(final int bytes, final int datablock, final int rsblock, final int skew) {
// calculate and append ecc code, and if necessary interleave
final int blocks = (bytes + 2) / datablock;
int b;
int n, p;
final ReedSolomon rs = new ReedSolomon();
 
rs.init_gf(0x12d);
rs.init_code(rsblock, 1);
 
for (b = 0; b < blocks; b++) {
final int[] buf = new int[256];
final int[] ecc = new int[256];
 
p = 0;
for (n = b; n < bytes; n += blocks) {
buf[p++] = this.target[n];
}
rs.encode(p, buf);
for (n = 0; n < rsblock; n++) {
ecc[n] = rs.getResult(n);
}
p = rsblock - 1; // comes back reversed
for (n = b; n < rsblock * blocks; n += blocks) {
if (skew == 1) {
/* Rotate ecc data to make 144x144 size symbols acceptable */
/* See http://groups.google.com/group/postscriptbarcode/msg/5ae8fda7757477da */
if (b < 8) {
this.target[bytes + n + 2] = ecc[p--];
} else {
this.target[bytes + n - 8] = ecc[p--];
}
} else {
this.target[bytes + n] = ecc[p--];
}
}
}
}
 
private void insertAt(final int pos, final char newbit) {
/* Insert a character into the middle of a string at position posn */
for (int i = this.binary_length; i > pos; i--) {
this.binary[i] = this.binary[i - 1];
}
this.binary[pos] = newbit;
this.binary_length++;
}
 
private void insertValueAt(final int posn, final int streamlen, final char newbit) {
int i;
 
for (i = streamlen; i > posn; i--) {
this.target[i] = this.target[i - 1];
}
this.target[posn] = newbit;
}
 
private void addPadBits(int tp, final int tail_length) {
int i, prn, temp;
 
for (i = tail_length; i > 0; i--) {
if (i == tail_length) {
this.target[tp] = 129;
tp++; /* Pad */
} else {
prn = 149 * (tp + 1) % 253 + 1;
temp = 129 + prn;
if (temp <= 254) {
this.target[tp] = temp;
tp++;
} else {
this.target[tp] = temp - 254;
tp++;
}
}
}
}
 
private void placeData(final int NR, final int NC) {
int r, c, p;
// invalidate
for (r = 0; r < NR; r++) {
for (c = 0; c < NC; c++) {
this.places[r * NC + c] = 0;
}
}
// start
p = 1;
r = 4;
c = 0;
do {
// check corner
if (r == NR && c == 0) {
placeCornerA(NR, NC, p++);
}
if (r == NR - 2 && c == 0 && NC % 4 != 0) {
placeCornerB(NR, NC, p++);
}
if (r == NR - 2 && c == 0 && NC % 8 == 4) {
placeCornerC(NR, NC, p++);
}
if (r == NR + 4 && c == 2 && NC % 8 == 0) {
placeCornerD(NR, NC, p++);
}
// up/right
do {
if (r < NR && c >= 0 && this.places[r * NC + c] == 0) {
placeBlock(NR, NC, r, c, p++);
}
r -= 2;
c += 2;
} while (r >= 0 && c < NC);
r++;
c += 3;
// down/left
do {
if (r >= 0 && c < NC && this.places[r * NC + c] == 0) {
placeBlock(NR, NC, r, c, p++);
}
r += 2;
c -= 2;
} while (r < NR && c >= 0);
r += 3;
c++;
} while (r < NR || c < NC);
// unfilled corner
if (this.places[NR * NC - 1] == 0) {
this.places[NR * NC - 1] = this.places[NR * NC - NC - 2] = 1;
}
}
 
private void placeCornerA(final int NR, final int NC, final int p) {
placeBit(NR, NC, NR - 1, 0, p, 7);
placeBit(NR, NC, NR - 1, 1, p, 6);
placeBit(NR, NC, NR - 1, 2, p, 5);
placeBit(NR, NC, 0, NC - 2, p, 4);
placeBit(NR, NC, 0, NC - 1, p, 3);
placeBit(NR, NC, 1, NC - 1, p, 2);
placeBit(NR, NC, 2, NC - 1, p, 1);
placeBit(NR, NC, 3, NC - 1, p, 0);
}
 
private void placeCornerB(final int NR, final int NC, final int p) {
placeBit(NR, NC, NR - 3, 0, p, 7);
placeBit(NR, NC, NR - 2, 0, p, 6);
placeBit(NR, NC, NR - 1, 0, p, 5);
placeBit(NR, NC, 0, NC - 4, p, 4);
placeBit(NR, NC, 0, NC - 3, p, 3);
placeBit(NR, NC, 0, NC - 2, p, 2);
placeBit(NR, NC, 0, NC - 1, p, 1);
placeBit(NR, NC, 1, NC - 1, p, 0);
}
 
private void placeCornerC(final int NR, final int NC, final int p) {
placeBit(NR, NC, NR - 3, 0, p, 7);
placeBit(NR, NC, NR - 2, 0, p, 6);
placeBit(NR, NC, NR - 1, 0, p, 5);
placeBit(NR, NC, 0, NC - 2, p, 4);
placeBit(NR, NC, 0, NC - 1, p, 3);
placeBit(NR, NC, 1, NC - 1, p, 2);
placeBit(NR, NC, 2, NC - 1, p, 1);
placeBit(NR, NC, 3, NC - 1, p, 0);
}
 
private void placeCornerD(final int NR, final int NC, final int p) {
placeBit(NR, NC, NR - 1, 0, p, 7);
placeBit(NR, NC, NR - 1, NC - 1, p, 6);
placeBit(NR, NC, 0, NC - 3, p, 5);
placeBit(NR, NC, 0, NC - 2, p, 4);
placeBit(NR, NC, 0, NC - 1, p, 3);
placeBit(NR, NC, 1, NC - 3, p, 2);
placeBit(NR, NC, 1, NC - 2, p, 1);
placeBit(NR, NC, 1, NC - 1, p, 0);
}
 
private void placeBlock(final int NR, final int NC, final int r, final int c, final int p) {
placeBit(NR, NC, r - 2, c - 2, p, 7);
placeBit(NR, NC, r - 2, c - 1, p, 6);
placeBit(NR, NC, r - 1, c - 2, p, 5);
placeBit(NR, NC, r - 1, c - 1, p, 4);
placeBit(NR, NC, r - 1, c - 0, p, 3);
placeBit(NR, NC, r - 0, c - 2, p, 2);
placeBit(NR, NC, r - 0, c - 1, p, 1);
placeBit(NR, NC, r - 0, c - 0, p, 0);
}
 
private void placeBit(final int NR, final int NC, int r, int c, final int p, final int b) {
if (r < 0) {
r += NR;
c += 4 - (NR + 4) % 8;
}
if (c < 0) {
c += NC;
r += 4 - (NC + 4) % 8;
}
this.places[r * NC + c] = (p << 3) + b;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/AztecRune.java
New file
0,0 → 1,160
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
/**
* <p>
* Implements the Aztec Runes bar code symbology according to ISO/IEC 24778:2008 Annex A.
*
* <p>
* Aztec Runes is a fixed-size matrix symbology which can encode whole integer values between 0 and
* 255.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class AztecRune extends Symbol {
 
private static final int[] BIT_PLACEMENT_MAP = { 1, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 29, 1, 0, 0, 0, 0, 0, 0, 0, 1, 9, 28, 1, 0, 1, 1, 1, 1, 1, 0, 1, 10, 27, 1, 0, 1,
0, 0, 0, 1, 0, 1, 11, 26, 1, 0, 1, 0, 1, 0, 1, 0, 1, 12, 25, 1, 0, 1, 0, 0, 0, 1, 0, 1, 13, 24, 1, 0, 1, 1, 1, 1, 1, 0, 1, 14, 23, 1, 0, 0, 0, 0, 0, 0, 0, 1, 15, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 0, 0, 22, 21, 20, 19, 18, 17, 16, 0, 0 };
 
@Override
protected void encode() {
 
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid input data");
}
 
int decimalValue = 0;
switch (this.content.length()) {
case 1:
decimalValue = this.content.charAt(0) - '0';
break;
case 2:
decimalValue = 10 * (this.content.charAt(0) - '0');
decimalValue += this.content.charAt(1) - '0';
break;
case 3:
decimalValue = 100 * (this.content.charAt(0) - '0');
decimalValue += 10 * (this.content.charAt(1) - '0');
decimalValue += this.content.charAt(2) - '0';
break;
default:
throw new OkapiException("Input too large");
}
 
if (decimalValue > 255) {
throw new OkapiException("Input too large");
}
 
final StringBuilder binaryDataStream = new StringBuilder(28);
for (int i = 0x80; i > 0; i = i >> 1) {
if ((decimalValue & i) != 0) {
binaryDataStream.append('1');
} else {
binaryDataStream.append('0');
}
}
 
final int[] dataCodeword = new int[3];
dataCodeword[0] = 0;
dataCodeword[1] = 0;
 
for (int i = 0; i < 2; i++) {
if (binaryDataStream.charAt(i * 4) == '1') {
dataCodeword[i] += 8;
}
if (binaryDataStream.charAt(i * 4 + 1) == '1') {
dataCodeword[i] += 4;
}
if (binaryDataStream.charAt(i * 4 + 2) == '1') {
dataCodeword[i] += 2;
}
if (binaryDataStream.charAt(i * 4 + 3) == '1') {
dataCodeword[i] += 1;
}
}
 
final int[] errorCorrectionCodeword = new int[6];
 
final ReedSolomon rs = new ReedSolomon();
rs.init_gf(0x13);
rs.init_code(5, 1);
rs.encode(2, dataCodeword);
 
for (int i = 0; i < 5; i++) {
errorCorrectionCodeword[i] = rs.getResult(i);
}
 
for (int i = 0; i < 5; i++) {
if ((errorCorrectionCodeword[4 - i] & 0x08) != 0) {
binaryDataStream.append('1');
} else {
binaryDataStream.append('0');
}
if ((errorCorrectionCodeword[4 - i] & 0x04) != 0) {
binaryDataStream.append('1');
} else {
binaryDataStream.append('0');
}
if ((errorCorrectionCodeword[4 - i] & 0x02) != 0) {
binaryDataStream.append('1');
} else {
binaryDataStream.append('0');
}
if ((errorCorrectionCodeword[4 - i] & 0x01) != 0) {
binaryDataStream.append('1');
} else {
binaryDataStream.append('0');
}
}
 
final StringBuilder reversedBinaryDataStream = new StringBuilder(28);
for (int i = 0; i < binaryDataStream.length(); i++) {
if ((i & 1) == 0) {
if (binaryDataStream.charAt(i) == '0') {
reversedBinaryDataStream.append('1');
} else {
reversedBinaryDataStream.append('0');
}
} else {
reversedBinaryDataStream.append(binaryDataStream.charAt(i));
}
}
 
infoLine("Binary: " + reversedBinaryDataStream);
 
this.readable = "";
this.pattern = new String[11];
this.row_count = 11;
this.row_height = new int[11];
 
for (int row = 0; row < 11; row++) {
final StringBuilder rowBinary = new StringBuilder(11);
for (int column = 0; column < 11; column++) {
if (BIT_PLACEMENT_MAP[row * 11 + column] == 1) {
rowBinary.append('1');
}
if (BIT_PLACEMENT_MAP[row * 11 + column] == 0) {
rowBinary.append('0');
}
if (BIT_PLACEMENT_MAP[row * 11 + column] >= 2) {
rowBinary.append(reversedBinaryDataStream.charAt(BIT_PLACEMENT_MAP[row * 11 + column] - 2));
}
}
this.pattern[row] = bin2pat(rowBinary);
this.row_height[row] = 1;
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Composite.java
New file
0,0 → 1,2769
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.util.Arrays.positionOf;
 
import java.awt.geom.Rectangle2D;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
 
import uk.org.okapibarcode.backend.DataBar14.Mode;
 
/**
* <p>
* Implements GS1 Composite symbology according to ISO/IEC 24723:2010.
*
* <p>
* Composite symbols comprise a 2D element which encodes GS1 data and a "linear" element which can
* be UPC, EAN, Code 128 or GS1 DataBar symbol.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Composite extends Symbol {
 
/** The linear component choices available. */
public static enum LinearEncoding {
UPCA, UPCE, EAN, CODE_128, DATABAR_14, DATABAR_14_STACK, DATABAR_14_STACK_OMNI, DATABAR_LIMITED, DATABAR_EXPANDED, DATABAR_EXPANDED_STACK
}
 
/** The 2D component choices available. */
public static enum CompositeMode {
/**
* Indicates that the composite symbol uses a MicroPDF417 variant as the 2D component. Of
* the 2D component choices, this one holds the least amount of data.
*/
CC_A,
/**
* Indicates that the composite symbol uses a MicroPDF417 symbol as the 2D component,
* starting with a codeword of 920.
*/
CC_B,
/**
* Indicates that the composite symbol uses a PDF417 symbol as the 2D component, starting
* with a codeword of 920. Of the 2D component choices, this one holds the most amount of
* data. May only be used if the linear component is {@link LinearEncoding#CODE_128 Code
* 128}.
*/
CC_C
}
 
private static enum GeneralFieldMode {
NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO
}
 
/* CC-A component coefficients from ISO/IEC 24728:2006 Annex F */
private static final int[] CCA_COEFFS = {
/* k = 4 */
522, 568, 723, 809,
/* k = 5 */
427, 919, 460, 155, 566,
/* k = 6 */
861, 285, 19, 803, 17, 766,
/* k = 7 */
76, 925, 537, 597, 784, 691, 437,
/* k = 8 */
237, 308, 436, 284, 646, 653, 428, 379 };
 
private static final int[] COEFRS = {
/* k = 2 */
27, 917,
/* k = 4 */
522, 568, 723, 809,
/* k = 8 */
237, 308, 436, 284, 646, 653, 428, 379,
/* k = 16 */
274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65,
/* k = 32 */
361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410,
/* k = 64 */
539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, 381, 612, 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, 400, 672, 762, 283, 184, 440, 35, 519, 31, 460, 594,
225, 535, 517, 352, 605, 158, 651, 201, 488, 502, 648, 733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843, 623, 264, 543,
/* k = 128 */
521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, 749, 415, 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, 292, 908, 490, 704, 516, 258, 457, 907, 594, 723,
674, 292, 272, 96, 684, 432, 686, 606, 860, 569, 193, 219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712, 463, 646, 776, 171, 491, 297, 763, 156, 732, 95, 270, 447, 90, 507, 48,
228, 821, 808, 898, 784, 663, 627, 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, 157, 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, 804, 34, 211, 330,
539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539,
/* k = 256 */
524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, 138, 720, 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, 280, 201, 280, 828, 757, 710, 814, 919, 89, 68, 569, 11,
204, 796, 605, 540, 913, 801, 700, 799, 137, 439, 418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284, 549, 209, 884, 315, 70, 329, 793, 490, 274, 877, 162, 749, 812, 684, 461, 334,
376, 849, 521, 307, 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470, 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, 538, 906, 90, 2, 290, 743, 199,
655, 903, 329, 49, 802, 580, 355, 588, 188, 462, 10, 134, 628, 320, 479, 130, 739, 71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234, 722, 384, 177, 752, 607, 640, 455, 193, 689, 707,
805, 641, 48, 60, 732, 621, 895, 544, 261, 852, 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528, 503, 118, 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550, 73,
914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, 893, 754, 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, 922, 191, 910, 532, 609, 829, 189, 20, 167, 29, 872, 449,
83, 402, 41, 656, 505, 579, 481, 173, 404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10,
/* k = 512 */
352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, 350, 492, 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, 87, 193, 352, 781, 846, 75, 327, 520, 435, 543,
203, 666, 249, 346, 781, 621, 640, 268, 794, 534, 539, 781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858, 916, 552, 41, 542, 289, 122, 272, 383, 800, 485, 98, 752, 472, 761, 107,
784, 860, 658, 741, 290, 204, 681, 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142, 808, 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, 258, 240, 518,
794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, 596, 786, 303, 570, 381, 415, 641, 156, 237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402, 40, 708, 575, 162, 864, 229, 65, 861,
841, 512, 164, 477, 221, 92, 358, 785, 288, 357, 850, 836, 827, 736, 707, 94, 8, 494, 114, 521, 2, 499, 851, 543, 152, 729, 771, 95, 248, 361, 578, 323, 856, 797, 289, 51, 684, 466, 533,
820, 669, 45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578, 37, 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, 911, 283, 711, 472, 420, 245,
288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408, 842, 383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713, 159, 672, 729, 624, 59, 193, 417, 158, 209, 563, 564, 343, 693, 109,
608, 563, 365, 181, 772, 677, 310, 248, 353, 708, 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777, 618, 586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45, 787,
680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, 905, 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341, 242, 797, 838, 837, 720, 224, 307,
631, 61, 87, 560, 310, 756, 665, 397, 808, 851, 309, 473, 795, 378, 31, 647, 915, 459, 806, 590, 731, 425, 216, 548, 249, 321, 881, 699, 535, 673, 782, 210, 815, 905, 303, 843, 922, 281,
73, 469, 791, 660, 162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437, 375, 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, 842, 37, 357, 720,
742, 330, 5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316, 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721, 610, 46, 656, 447, 171, 616, 464, 190, 531, 297, 321, 762,
752, 533, 175, 134, 14, 381, 433, 717, 45, 111, 20, 596, 284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780, 407, 164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768, 223, 849,
647, 63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263 };
 
/* rows, error codewords, k-offset of valid CC-A sizes from ISO/IEC 24723:2006 Table 9 */
private static final int[] CCA_VARIANTS = { 5, 6, 7, 8, 9, 10, 12, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 4, 4, 5, 5, 6, 6, 7, 4, 5, 6, 7, 7, 4, 5, 6, 7, 8, 0, 0, 4, 4, 9, 9, 15, 0, 4, 9, 15, 15, 0, 4, 9,
15, 22 };
 
/*
* following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24723:2006 tables
* 10 and 11
*/
private static final int[] A_RAP_TABLE = { 39, 1, 32, 8, 14, 43, 20, 11, 1, 5, 15, 21, 40, 43, 46, 34, 29, 0, 0, 0, 0, 0, 0, 0, 43, 33, 37, 47, 1, 20, 23, 26, 14, 9, 19, 33, 12, 40, 46, 23, 52,
23, 13, 17, 27, 33, 52, 3, 6, 46, 41, 6, 0, 3, 3, 3, 0, 3, 3, 0, 3, 6, 6, 0, 0, 0, 0, 3 };
 
private static final String[] CODAGEMC = { "urA", "xfs", "ypy", "unk", "xdw", "yoz", "pDA", "uls", "pBk", "eBA", "pAs", "eAk", "prA", "uvs", "xhy", "pnk", "utw", "xgz", "fDA", "pls", "fBk", "frA",
"pvs", "uxy", "fnk", "ptw", "uwz", "fls", "psy", "fvs", "pxy", "ftw", "pwz", "fxy", "yrx", "ufk", "xFw", "ymz", "onA", "uds", "xEy", "olk", "ucw", "dBA", "oks", "uci", "dAk", "okg", "dAc",
"ovk", "uhw", "xaz", "dnA", "ots", "ugy", "dlk", "osw", "ugj", "dks", "osi", "dvk", "oxw", "uiz", "dts", "owy", "dsw", "owj", "dxw", "oyz", "dwy", "dwj", "ofA", "uFs", "xCy", "odk", "uEw",
"xCj", "clA", "ocs", "uEi", "ckk", "ocg", "ckc", "ckE", "cvA", "ohs", "uay", "ctk", "ogw", "uaj", "css", "ogi", "csg", "csa", "cxs", "oiy", "cww", "oij", "cwi", "cyy", "oFk", "uCw", "xBj",
"cdA", "oEs", "uCi", "cck", "oEg", "uCb", "ccc", "oEa", "ccE", "oED", "chk", "oaw", "uDj", "cgs", "oai", "cgg", "oab", "cga", "cgD", "obj", "cib", "cFA", "oCs", "uBi", "cEk", "oCg", "uBb",
"cEc", "oCa", "cEE", "oCD", "cEC", "cas", "cag", "caa", "cCk", "uAr", "oBa", "oBD", "cCB", "tfk", "wpw", "yez", "mnA", "tds", "woy", "mlk", "tcw", "woj", "FBA", "mks", "FAk", "mvk", "thw",
"wqz", "FnA", "mts", "tgy", "Flk", "msw", "Fks", "Fkg", "Fvk", "mxw", "tiz", "Fts", "mwy", "Fsw", "Fsi", "Fxw", "myz", "Fwy", "Fyz", "vfA", "xps", "yuy", "vdk", "xow", "yuj", "qlA", "vcs",
"xoi", "qkk", "vcg", "xob", "qkc", "vca", "mfA", "tFs", "wmy", "qvA", "mdk", "tEw", "wmj", "qtk", "vgw", "xqj", "hlA", "Ekk", "mcg", "tEb", "hkk", "qsg", "hkc", "EvA", "mhs", "tay", "hvA",
"Etk", "mgw", "taj", "htk", "qww", "vij", "hss", "Esg", "hsg", "Exs", "miy", "hxs", "Eww", "mij", "hww", "qyj", "hwi", "Eyy", "hyy", "Eyj", "hyj", "vFk", "xmw", "ytj", "qdA", "vEs", "xmi",
"qck", "vEg", "xmb", "qcc", "vEa", "qcE", "qcC", "mFk", "tCw", "wlj", "qhk", "mEs", "tCi", "gtA", "Eck", "vai", "tCb", "gsk", "Ecc", "mEa", "gsc", "qga", "mED", "EcC", "Ehk", "maw", "tDj",
"gxk", "Egs", "mai", "gws", "qii", "mab", "gwg", "Ega", "EgD", "Eiw", "mbj", "gyw", "Eii", "gyi", "Eib", "gyb", "gzj", "qFA", "vCs", "xli", "qEk", "vCg", "xlb", "qEc", "vCa", "qEE", "vCD",
"qEC", "qEB", "EFA", "mCs", "tBi", "ghA", "EEk", "mCg", "tBb", "ggk", "qag", "vDb", "ggc", "EEE", "mCD", "ggE", "qaD", "ggC", "Eas", "mDi", "gis", "Eag", "mDb", "gig", "qbb", "gia", "EaD",
"giD", "gji", "gjb", "qCk", "vBg", "xkr", "qCc", "vBa", "qCE", "vBD", "qCC", "qCB", "ECk", "mBg", "tAr", "gak", "ECc", "mBa", "gac", "qDa", "mBD", "gaE", "ECC", "gaC", "ECB", "EDg", "gbg",
"gba", "gbD", "vAq", "vAn", "qBB", "mAq", "EBE", "gDE", "gDC", "gDB", "lfA", "sps", "wey", "ldk", "sow", "ClA", "lcs", "soi", "Ckk", "lcg", "Ckc", "CkE", "CvA", "lhs", "sqy", "Ctk", "lgw",
"sqj", "Css", "lgi", "Csg", "Csa", "Cxs", "liy", "Cww", "lij", "Cwi", "Cyy", "Cyj", "tpk", "wuw", "yhj", "ndA", "tos", "wui", "nck", "tog", "wub", "ncc", "toa", "ncE", "toD", "lFk", "smw",
"wdj", "nhk", "lEs", "smi", "atA", "Cck", "tqi", "smb", "ask", "ngg", "lEa", "asc", "CcE", "asE", "Chk", "law", "snj", "axk", "Cgs", "trj", "aws", "nii", "lab", "awg", "Cga", "awa", "Ciw",
"lbj", "ayw", "Cii", "ayi", "Cib", "Cjj", "azj", "vpA", "xus", "yxi", "vok", "xug", "yxb", "voc", "xua", "voE", "xuD", "voC", "nFA", "tms", "wti", "rhA", "nEk", "xvi", "wtb", "rgk", "vqg",
"xvb", "rgc", "nEE", "tmD", "rgE", "vqD", "nEB", "CFA", "lCs", "sli", "ahA", "CEk", "lCg", "slb", "ixA", "agk", "nag", "tnb", "iwk", "rig", "vrb", "lCD", "iwc", "agE", "naD", "iwE", "CEB",
"Cas", "lDi", "ais", "Cag", "lDb", "iys", "aig", "nbb", "iyg", "rjb", "CaD", "aiD", "Cbi", "aji", "Cbb", "izi", "ajb", "vmk", "xtg", "ywr", "vmc", "xta", "vmE", "xtD", "vmC", "vmB", "nCk",
"tlg", "wsr", "rak", "nCc", "xtr", "rac", "vna", "tlD", "raE", "nCC", "raC", "nCB", "raB", "CCk", "lBg", "skr", "aak", "CCc", "lBa", "iik", "aac", "nDa", "lBD", "iic", "rba", "CCC", "iiE",
"aaC", "CCB", "aaB", "CDg", "lBr", "abg", "CDa", "ijg", "aba", "CDD", "ija", "abD", "CDr", "ijr", "vlc", "xsq", "vlE", "xsn", "vlC", "vlB", "nBc", "tkq", "rDc", "nBE", "tkn", "rDE", "vln",
"rDC", "nBB", "rDB", "CBc", "lAq", "aDc", "CBE", "lAn", "ibc", "aDE", "nBn", "ibE", "rDn", "CBB", "ibC", "aDB", "ibB", "aDq", "ibq", "ibn", "xsf", "vkl", "tkf", "nAm", "nAl", "CAo", "aBo",
"iDo", "CAl", "aBl", "kpk", "BdA", "kos", "Bck", "kog", "seb", "Bcc", "koa", "BcE", "koD", "Bhk", "kqw", "sfj", "Bgs", "kqi", "Bgg", "kqb", "Bga", "BgD", "Biw", "krj", "Bii", "Bib", "Bjj",
"lpA", "sus", "whi", "lok", "sug", "loc", "sua", "loE", "suD", "loC", "BFA", "kms", "sdi", "DhA", "BEk", "svi", "sdb", "Dgk", "lqg", "svb", "Dgc", "BEE", "kmD", "DgE", "lqD", "BEB", "Bas",
"kni", "Dis", "Bag", "knb", "Dig", "lrb", "Dia", "BaD", "Bbi", "Dji", "Bbb", "Djb", "tuk", "wxg", "yir", "tuc", "wxa", "tuE", "wxD", "tuC", "tuB", "lmk", "stg", "nqk", "lmc", "sta", "nqc",
"tva", "stD", "nqE", "lmC", "nqC", "lmB", "nqB", "BCk", "klg", "Dak", "BCc", "str", "bik", "Dac", "lna", "klD", "bic", "nra", "BCC", "biE", "DaC", "BCB", "DaB", "BDg", "klr", "Dbg", "BDa",
"bjg", "Dba", "BDD", "bja", "DbD", "BDr", "Dbr", "bjr", "xxc", "yyq", "xxE", "yyn", "xxC", "xxB", "ttc", "wwq", "vvc", "xxq", "wwn", "vvE", "xxn", "vvC", "ttB", "vvB", "llc", "ssq", "nnc",
"llE", "ssn", "rrc", "nnE", "ttn", "rrE", "vvn", "llB", "rrC", "nnB", "rrB", "BBc", "kkq", "DDc", "BBE", "kkn", "bbc", "DDE", "lln", "jjc", "bbE", "nnn", "BBB", "jjE", "rrn", "DDB", "jjC",
"BBq", "DDq", "BBn", "bbq", "DDn", "jjq", "bbn", "jjn", "xwo", "yyf", "xwm", "xwl", "tso", "wwf", "vto", "xwv", "vtm", "tsl", "vtl", "lko", "ssf", "nlo", "lkm", "rno", "nlm", "lkl", "rnm",
"nll", "rnl", "BAo", "kkf", "DBo", "lkv", "bDo", "DBm", "BAl", "jbo", "bDm", "DBl", "jbm", "bDl", "jbl", "DBv", "jbv", "xwd", "vsu", "vst", "nku", "rlu", "rlt", "DAu", "bBu", "jDu", "jDt",
"ApA", "Aok", "keg", "Aoc", "AoE", "AoC", "Aqs", "Aqg", "Aqa", "AqD", "Ari", "Arb", "kuk", "kuc", "sha", "kuE", "shD", "kuC", "kuB", "Amk", "kdg", "Bqk", "kvg", "kda", "Bqc", "kva", "BqE",
"kvD", "BqC", "AmB", "BqB", "Ang", "kdr", "Brg", "kvr", "Bra", "AnD", "BrD", "Anr", "Brr", "sxc", "sxE", "sxC", "sxB", "ktc", "lvc", "sxq", "sgn", "lvE", "sxn", "lvC", "ktB", "lvB", "Alc",
"Bnc", "AlE", "kcn", "Drc", "BnE", "AlC", "DrE", "BnC", "AlB", "DrC", "BnB", "Alq", "Bnq", "Aln", "Drq", "Bnn", "Drn", "wyo", "wym", "wyl", "swo", "txo", "wyv", "txm", "swl", "txl", "kso",
"sgf", "lto", "swv", "nvo", "ltm", "ksl", "nvm", "ltl", "nvl", "Ako", "kcf", "Blo", "ksv", "Dno", "Blm", "Akl", "bro", "Dnm", "Bll", "brm", "Dnl", "Akv", "Blv", "Dnv", "brv", "yze", "yzd",
"wye", "xyu", "wyd", "xyt", "swe", "twu", "swd", "vxu", "twt", "vxt", "kse", "lsu", "ksd", "ntu", "lst", "rvu", "ypk", "zew", "xdA", "yos", "zei", "xck", "yog", "zeb", "xcc", "yoa", "xcE",
"yoD", "xcC", "xhk", "yqw", "zfj", "utA", "xgs", "yqi", "usk", "xgg", "yqb", "usc", "xga", "usE", "xgD", "usC", "uxk", "xiw", "yrj", "ptA", "uws", "xii", "psk", "uwg", "xib", "psc", "uwa",
"psE", "uwD", "psC", "pxk", "uyw", "xjj", "ftA", "pws", "uyi", "fsk", "pwg", "uyb", "fsc", "pwa", "fsE", "pwD", "fxk", "pyw", "uzj", "fws", "pyi", "fwg", "pyb", "fwa", "fyw", "pzj", "fyi",
"fyb", "xFA", "yms", "zdi", "xEk", "ymg", "zdb", "xEc", "yma", "xEE", "ymD", "xEC", "xEB", "uhA", "xas", "yni", "ugk", "xag", "ynb", "ugc", "xaa", "ugE", "xaD", "ugC", "ugB", "oxA", "uis",
"xbi", "owk", "uig", "xbb", "owc", "uia", "owE", "uiD", "owC", "owB", "dxA", "oys", "uji", "dwk", "oyg", "ujb", "dwc", "oya", "dwE", "oyD", "dwC", "dys", "ozi", "dyg", "ozb", "dya", "dyD",
"dzi", "dzb", "xCk", "ylg", "zcr", "xCc", "yla", "xCE", "ylD", "xCC", "xCB", "uak", "xDg", "ylr", "uac", "xDa", "uaE", "xDD", "uaC", "uaB", "oik", "ubg", "xDr", "oic", "uba", "oiE", "ubD",
"oiC", "oiB", "cyk", "ojg", "ubr", "cyc", "oja", "cyE", "ojD", "cyC", "cyB", "czg", "ojr", "cza", "czD", "czr", "xBc", "ykq", "xBE", "ykn", "xBC", "xBB", "uDc", "xBq", "uDE", "xBn", "uDC",
"uDB", "obc", "uDq", "obE", "uDn", "obC", "obB", "cjc", "obq", "cjE", "obn", "cjC", "cjB", "cjq", "cjn", "xAo", "ykf", "xAm", "xAl", "uBo", "xAv", "uBm", "uBl", "oDo", "uBv", "oDm", "oDl",
"cbo", "oDv", "cbm", "cbl", "xAe", "xAd", "uAu", "uAt", "oBu", "oBt", "wpA", "yes", "zFi", "wok", "yeg", "zFb", "woc", "yea", "woE", "yeD", "woC", "woB", "thA", "wqs", "yfi", "tgk", "wqg",
"yfb", "tgc", "wqa", "tgE", "wqD", "tgC", "tgB", "mxA", "tis", "wri", "mwk", "tig", "wrb", "mwc", "tia", "mwE", "tiD", "mwC", "mwB", "FxA", "mys", "tji", "Fwk", "myg", "tjb", "Fwc", "mya",
"FwE", "myD", "FwC", "Fys", "mzi", "Fyg", "mzb", "Fya", "FyD", "Fzi", "Fzb", "yuk", "zhg", "hjs", "yuc", "zha", "hbw", "yuE", "zhD", "hDy", "yuC", "yuB", "wmk", "ydg", "zEr", "xqk", "wmc",
"zhr", "xqc", "yva", "ydD", "xqE", "wmC", "xqC", "wmB", "xqB", "tak", "wng", "ydr", "vik", "tac", "wna", "vic", "xra", "wnD", "viE", "taC", "viC", "taB", "viB", "mik", "tbg", "wnr", "qyk",
"mic", "tba", "qyc", "vja", "tbD", "qyE", "miC", "qyC", "miB", "qyB", "Eyk", "mjg", "tbr", "hyk", "Eyc", "mja", "hyc", "qza", "mjD", "hyE", "EyC", "hyC", "EyB", "Ezg", "mjr", "hzg", "Eza",
"hza", "EzD", "hzD", "Ezr", "ytc", "zgq", "grw", "ytE", "zgn", "gny", "ytC", "glz", "ytB", "wlc", "ycq", "xnc", "wlE", "ycn", "xnE", "ytn", "xnC", "wlB", "xnB", "tDc", "wlq", "vbc", "tDE",
"wln", "vbE", "xnn", "vbC", "tDB", "vbB", "mbc", "tDq", "qjc", "mbE", "tDn", "qjE", "vbn", "qjC", "mbB", "qjB", "Ejc", "mbq", "gzc", "EjE", "mbn", "gzE", "qjn", "gzC", "EjB", "gzB", "Ejq",
"gzq", "Ejn", "gzn", "yso", "zgf", "gfy", "ysm", "gdz", "ysl", "wko", "ycf", "xlo", "ysv", "xlm", "wkl", "xll", "tBo", "wkv", "vDo", "tBm", "vDm", "tBl", "vDl", "mDo", "tBv", "qbo", "vDv",
"qbm", "mDl", "qbl", "Ebo", "mDv", "gjo", "Ebm", "gjm", "Ebl", "gjl", "Ebv", "gjv", "yse", "gFz", "ysd", "wke", "xku", "wkd", "xkt", "tAu", "vBu", "tAt", "vBt", "mBu", "qDu", "mBt", "qDt",
"EDu", "gbu", "EDt", "gbt", "ysF", "wkF", "xkh", "tAh", "vAx", "mAx", "qBx", "wek", "yFg", "zCr", "wec", "yFa", "weE", "yFD", "weC", "weB", "sqk", "wfg", "yFr", "sqc", "wfa", "sqE", "wfD",
"sqC", "sqB", "lik", "srg", "wfr", "lic", "sra", "liE", "srD", "liC", "liB", "Cyk", "ljg", "srr", "Cyc", "lja", "CyE", "ljD", "CyC", "CyB", "Czg", "ljr", "Cza", "CzD", "Czr", "yhc", "zaq",
"arw", "yhE", "zan", "any", "yhC", "alz", "yhB", "wdc", "yEq", "wvc", "wdE", "yEn", "wvE", "yhn", "wvC", "wdB", "wvB", "snc", "wdq", "trc", "snE", "wdn", "trE", "wvn", "trC", "snB", "trB",
"lbc", "snq", "njc", "lbE", "snn", "njE", "trn", "njC", "lbB", "njB", "Cjc", "lbq", "azc", "CjE", "lbn", "azE", "njn", "azC", "CjB", "azB", "Cjq", "azq", "Cjn", "azn", "zio", "irs", "rfy",
"zim", "inw", "rdz", "zil", "ily", "ikz", "ygo", "zaf", "afy", "yxo", "ziv", "ivy", "adz", "yxm", "ygl", "itz", "yxl", "wco", "yEf", "wto", "wcm", "xvo", "yxv", "wcl", "xvm", "wtl", "xvl",
"slo", "wcv", "tno", "slm", "vro", "tnm", "sll", "vrm", "tnl", "vrl", "lDo", "slv", "nbo", "lDm", "rjo", "nbm", "lDl", "rjm", "nbl", "rjl", "Cbo", "lDv", "ajo", "Cbm", "izo", "ajm", "Cbl",
"izm", "ajl", "izl", "Cbv", "ajv", "zie", "ifw", "rFz", "zid", "idy", "icz", "yge", "aFz", "ywu", "ygd", "ihz", "ywt", "wce", "wsu", "wcd", "xtu", "wst", "xtt", "sku", "tlu", "skt", "vnu",
"tlt", "vnt", "lBu", "nDu", "lBt", "rbu", "nDt", "rbt", "CDu", "abu", "CDt", "iju", "abt", "ijt", "ziF", "iFy", "iEz", "ygF", "ywh", "wcF", "wsh", "xsx", "skh", "tkx", "vlx", "lAx", "nBx",
"rDx", "CBx", "aDx", "ibx", "iCz", "wFc", "yCq", "wFE", "yCn", "wFC", "wFB", "sfc", "wFq", "sfE", "wFn", "sfC", "sfB", "krc", "sfq", "krE", "sfn", "krC", "krB", "Bjc", "krq", "BjE", "krn",
"BjC", "BjB", "Bjq", "Bjn", "yao", "zDf", "Dfy", "yam", "Ddz", "yal", "wEo", "yCf", "who", "wEm", "whm", "wEl", "whl", "sdo", "wEv", "svo", "sdm", "svm", "sdl", "svl", "kno", "sdv", "lro",
"knm", "lrm", "knl", "lrl", "Bbo", "knv", "Djo", "Bbm", "Djm", "Bbl", "Djl", "Bbv", "Djv", "zbe", "bfw", "npz", "zbd", "bdy", "bcz", "yae", "DFz", "yiu", "yad", "bhz", "yit", "wEe", "wgu",
"wEd", "wxu", "wgt", "wxt", "scu", "stu", "sct", "tvu", "stt", "tvt", "klu", "lnu", "klt", "nru", "lnt", "nrt", "BDu", "Dbu", "BDt", "bju", "Dbt", "bjt", "jfs", "rpy", "jdw", "roz", "jcy",
"jcj", "zbF", "bFy", "zjh", "jhy", "bEz", "jgz", "yaF", "yih", "yyx", "wEF", "wgh", "wwx", "xxx", "sch", "ssx", "ttx", "vvx", "kkx", "llx", "nnx", "rrx", "BBx", "DDx", "bbx", "jFw", "rmz",
"jEy", "jEj", "bCz", "jaz", "jCy", "jCj", "jBj", "wCo", "wCm", "wCl", "sFo", "wCv", "sFm", "sFl", "kfo", "sFv", "kfm", "kfl", "Aro", "kfv", "Arm", "Arl", "Arv", "yDe", "Bpz", "yDd", "wCe",
"wau", "wCd", "wat", "sEu", "shu", "sEt", "sht", "kdu", "kvu", "kdt", "kvt", "Anu", "Bru", "Ant", "Brt", "zDp", "Dpy", "Doz", "yDF", "ybh", "wCF", "wah", "wix", "sEh", "sgx", "sxx", "kcx",
"ktx", "lvx", "Alx", "Bnx", "Drx", "bpw", "nuz", "boy", "boj", "Dmz", "bqz", "jps", "ruy", "jow", "ruj", "joi", "job", "bmy", "jqy", "bmj", "jqj", "jmw", "rtj", "jmi", "jmb", "blj", "jnj",
"jli", "jlb", "jkr", "sCu", "sCt", "kFu", "kFt", "Afu", "Aft", "wDh", "sCh", "sax", "kEx", "khx", "Adx", "Avx", "Buz", "Duy", "Duj", "buw", "nxj", "bui", "bub", "Dtj", "bvj", "jus", "rxi",
"jug", "rxb", "jua", "juD", "bti", "jvi", "btb", "jvb", "jtg", "rwr", "jta", "jtD", "bsr", "jtr", "jsq", "jsn", "Bxj", "Dxi", "Dxb", "bxg", "nyr", "bxa", "bxD", "Dwr", "bxr", "bwq", "bwn",
"pjk", "urw", "ejA", "pbs", "uny", "ebk", "pDw", "ulz", "eDs", "pBy", "eBw", "zfc", "fjk", "prw", "zfE", "fbs", "pny", "zfC", "fDw", "plz", "zfB", "fBy", "yrc", "zfq", "frw", "yrE", "zfn",
"fny", "yrC", "flz", "yrB", "xjc", "yrq", "xjE", "yrn", "xjC", "xjB", "uzc", "xjq", "uzE", "xjn", "uzC", "uzB", "pzc", "uzq", "pzE", "uzn", "pzC", "djA", "ors", "ufy", "dbk", "onw", "udz",
"dDs", "oly", "dBw", "okz", "dAy", "zdo", "drs", "ovy", "zdm", "dnw", "otz", "zdl", "dly", "dkz", "yno", "zdv", "dvy", "ynm", "dtz", "ynl", "xbo", "ynv", "xbm", "xbl", "ujo", "xbv", "ujm",
"ujl", "ozo", "ujv", "ozm", "ozl", "crk", "ofw", "uFz", "cns", "ody", "clw", "ocz", "cky", "ckj", "zcu", "cvw", "ohz", "zct", "cty", "csz", "ylu", "cxz", "ylt", "xDu", "xDt", "ubu", "ubt",
"oju", "ojt", "cfs", "oFy", "cdw", "oEz", "ccy", "ccj", "zch", "chy", "cgz", "ykx", "xBx", "uDx", "cFw", "oCz", "cEy", "cEj", "caz", "cCy", "cCj", "FjA", "mrs", "tfy", "Fbk", "mnw", "tdz",
"FDs", "mly", "FBw", "mkz", "FAy", "zFo", "Frs", "mvy", "zFm", "Fnw", "mtz", "zFl", "Fly", "Fkz", "yfo", "zFv", "Fvy", "yfm", "Ftz", "yfl", "wro", "yfv", "wrm", "wrl", "tjo", "wrv", "tjm",
"tjl", "mzo", "tjv", "mzm", "mzl", "qrk", "vfw", "xpz", "hbA", "qns", "vdy", "hDk", "qlw", "vcz", "hBs", "qky", "hAw", "qkj", "hAi", "Erk", "mfw", "tFz", "hrk", "Ens", "mdy", "hns", "qty",
"mcz", "hlw", "Eky", "hky", "Ekj", "hkj", "zEu", "Evw", "mhz", "zhu", "zEt", "hvw", "Ety", "zht", "hty", "Esz", "hsz", "ydu", "Exz", "yvu", "ydt", "hxz", "yvt", "wnu", "xru", "wnt", "xrt",
"tbu", "vju", "tbt", "vjt", "mju", "mjt", "grA", "qfs", "vFy", "gnk", "qdw", "vEz", "gls", "qcy", "gkw", "qcj", "gki", "gkb", "Efs", "mFy", "gvs", "Edw", "mEz", "gtw", "qgz", "gsy", "Ecj",
"gsj", "zEh", "Ehy", "zgx", "gxy", "Egz", "gwz", "ycx", "ytx", "wlx", "xnx", "tDx", "vbx", "mbx", "gfk", "qFw", "vCz", "gds", "qEy", "gcw", "qEj", "gci", "gcb", "EFw", "mCz", "ghw", "EEy",
"ggy", "EEj", "ggj", "Eaz", "giz", "gFs", "qCy", "gEw", "qCj", "gEi", "gEb", "ECy", "gay", "ECj", "gaj", "gCw", "qBj", "gCi", "gCb", "EBj", "gDj", "gBi", "gBb", "Crk", "lfw", "spz", "Cns",
"ldy", "Clw", "lcz", "Cky", "Ckj", "zCu", "Cvw", "lhz", "zCt", "Cty", "Csz", "yFu", "Cxz", "yFt", "wfu", "wft", "sru", "srt", "lju", "ljt", "arA", "nfs", "tpy", "ank", "ndw", "toz", "als",
"ncy", "akw", "ncj", "aki", "akb", "Cfs", "lFy", "avs", "Cdw", "lEz", "atw", "ngz", "asy", "Ccj", "asj", "zCh", "Chy", "zax", "axy", "Cgz", "awz", "yEx", "yhx", "wdx", "wvx", "snx", "trx",
"lbx", "rfk", "vpw", "xuz", "inA", "rds", "voy", "ilk", "rcw", "voj", "iks", "rci", "ikg", "rcb", "ika", "afk", "nFw", "tmz", "ivk", "ads", "nEy", "its", "rgy", "nEj", "isw", "aci", "isi",
"acb", "isb", "CFw", "lCz", "ahw", "CEy", "ixw", "agy", "CEj", "iwy", "agj", "iwj", "Caz", "aiz", "iyz", "ifA", "rFs", "vmy", "idk", "rEw", "vmj", "ics", "rEi", "icg", "rEb", "ica", "icD",
"aFs", "nCy", "ihs", "aEw", "nCj", "igw", "raj", "igi", "aEb", "igb", "CCy", "aay", "CCj", "iiy", "aaj", "iij", "iFk", "rCw", "vlj", "iEs", "rCi", "iEg", "rCb", "iEa", "iED", "aCw", "nBj",
"iaw", "aCi", "iai", "aCb", "iab", "CBj", "aDj", "ibj", "iCs", "rBi", "iCg", "rBb", "iCa", "iCD", "aBi", "iDi", "aBb", "iDb", "iBg", "rAr", "iBa", "iBD", "aAr", "iBr", "iAq", "iAn", "Bfs",
"kpy", "Bdw", "koz", "Bcy", "Bcj", "Bhy", "Bgz", "yCx", "wFx", "sfx", "krx", "Dfk", "lpw", "suz", "Dds", "loy", "Dcw", "loj", "Dci", "Dcb", "BFw", "kmz", "Dhw", "BEy", "Dgy", "BEj", "Dgj",
"Baz", "Diz", "bfA", "nps", "tuy", "bdk", "now", "tuj", "bcs", "noi", "bcg", "nob", "bca", "bcD", "DFs", "lmy", "bhs", "DEw", "lmj", "bgw", "DEi", "bgi", "DEb", "bgb", "BCy", "Day", "BCj",
"biy", "Daj", "bij", "rpk", "vuw", "xxj", "jdA", "ros", "vui", "jck", "rog", "vub", "jcc", "roa", "jcE", "roD", "jcC", "bFk", "nmw", "ttj", "jhk", "bEs", "nmi", "jgs", "rqi", "nmb", "jgg",
"bEa", "jga", "bED", "jgD", "DCw", "llj", "baw", "DCi", "jiw", "bai", "DCb", "jii", "bab", "jib", "BBj", "DDj", "bbj", "jjj", "jFA", "rms", "vti", "jEk", "rmg", "vtb", "jEc", "rma", "jEE",
"rmD", "jEC", "jEB", "bCs", "nli", "jas", "bCg", "nlb", "jag", "rnb", "jaa", "bCD", "jaD", "DBi", "bDi", "DBb", "jbi", "bDb", "jbb", "jCk", "rlg", "vsr", "jCc", "rla", "jCE", "rlD", "jCC",
"jCB", "bBg", "nkr", "jDg", "bBa", "jDa", "bBD", "jDD", "DAr", "bBr", "jDr", "jBc", "rkq", "jBE", "rkn", "jBC", "jBB", "bAq", "jBq", "bAn", "jBn", "jAo", "rkf", "jAm", "jAl", "bAf", "jAv",
"Apw", "kez", "Aoy", "Aoj", "Aqz", "Bps", "kuy", "Bow", "kuj", "Boi", "Bob", "Amy", "Bqy", "Amj", "Bqj", "Dpk", "luw", "sxj", "Dos", "lui", "Dog", "lub", "Doa", "DoD", "Bmw", "ktj", "Dqw",
"Bmi", "Dqi", "Bmb", "Dqb", "Alj", "Bnj", "Drj", "bpA", "nus", "txi", "bok", "nug", "txb", "boc", "nua", "boE", "nuD", "boC", "boB", "Dms", "lti", "bqs", "Dmg", "ltb", "bqg", "nvb", "bqa",
"DmD", "bqD", "Bli", "Dni", "Blb", "bri", "Dnb", "brb", "ruk", "vxg", "xyr", "ruc", "vxa", "ruE", "vxD", "ruC", "ruB", "bmk", "ntg", "twr", "jqk", "bmc", "nta", "jqc", "rva", "ntD", "jqE",
"bmC", "jqC", "bmB", "jqB", "Dlg", "lsr", "bng", "Dla", "jrg", "bna", "DlD", "jra", "bnD", "jrD", "Bkr", "Dlr", "bnr", "jrr", "rtc", "vwq", "rtE", "vwn", "rtC", "rtB", "blc", "nsq", "jnc",
"blE", "nsn", "jnE", "rtn", "jnC", "blB", "jnB", "Dkq", "blq", "Dkn", "jnq", "bln", "jnn", "rso", "vwf", "rsm", "rsl", "bko", "nsf", "jlo", "bkm", "jlm", "bkl", "jll", "Dkf", "bkv", "jlv",
"rse", "rsd", "bke", "jku", "bkd", "jkt", "Aey", "Aej", "Auw", "khj", "Aui", "Aub", "Adj", "Avj", "Bus", "kxi", "Bug", "kxb", "Bua", "BuD", "Ati", "Bvi", "Atb", "Bvb", "Duk", "lxg", "syr",
"Duc", "lxa", "DuE", "lxD", "DuC", "DuB", "Btg", "kwr", "Dvg", "lxr", "Dva", "BtD", "DvD", "Asr", "Btr", "Dvr", "nxc", "tyq", "nxE", "tyn", "nxC", "nxB", "Dtc", "lwq", "bvc", "nxq", "lwn",
"bvE", "DtC", "bvC", "DtB", "bvB", "Bsq", "Dtq", "Bsn", "bvq", "Dtn", "bvn", "vyo", "xzf", "vym", "vyl", "nwo", "tyf", "rxo", "nwm", "rxm", "nwl", "rxl", "Dso", "lwf", "bto", "Dsm", "jvo",
"btm", "Dsl", "jvm", "btl", "jvl", "Bsf", "Dsv", "btv", "jvv", "vye", "vyd", "nwe", "rwu", "nwd", "rwt", "Dse", "bsu", "Dsd", "jtu", "bst", "jtt", "vyF", "nwF", "rwh", "DsF", "bsh", "jsx",
"Ahi", "Ahb", "Axg", "kir", "Axa", "AxD", "Agr", "Axr", "Bxc", "kyq", "BxE", "kyn", "BxC", "BxB", "Awq", "Bxq", "Awn", "Bxn", "lyo", "szf", "lym", "lyl", "Bwo", "kyf", "Dxo", "lyv", "Dxm",
"Bwl", "Dxl", "Awf", "Bwv", "Dxv", "tze", "tzd", "lye", "nyu", "lyd", "nyt", "Bwe", "Dwu", "Bwd", "bxu", "Dwt", "bxt", "tzF", "lyF", "nyh", "BwF", "Dwh", "bwx", "Aiq", "Ain", "Ayo", "kjf",
"Aym", "Ayl", "Aif", "Ayv", "kze", "kzd", "Aye", "Byu", "Ayd", "Byt", "szp" };
 
private static final char[] BR_SET = { 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
'z', '*', '+', '-' };
 
private static final String[] PDF_TTF = { "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000",
"10001", "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010", "11011", "11100", "11101", "11110", "11111", "01", "1111111101010100", "11111101000101001" };
 
/* Left and Right Row Address Pattern from Table 2 */
private static final String[] RAPLR = { "", "221311", "311311", "312211", "222211", "213211", "214111", "223111", "313111", "322111", "412111", "421111", "331111", "241111", "232111", "231211",
"321211", "411211", "411121", "411112", "321112", "312112", "311212", "311221", "311131", "311122", "311113", "221113", "221122", "221131", "221221", "222121", "312121", "321121",
"231121", "231112", "222112", "213112", "212212", "212221", "212131", "212122", "212113", "211213", "211123", "211132", "211141", "211231", "211222", "211312", "211321", "211411",
"212311" };
 
/* Centre Row Address Pattern from Table 2 */
private static final String[] RAPC = { "", "112231", "121231", "122131", "131131", "131221", "132121", "141121", "141211", "142111", "133111", "132211", "131311", "122311", "123211", "124111",
"115111", "114211", "114121", "123121", "123112", "122212", "122221", "121321", "121411", "112411", "113311", "113221", "113212", "113122", "122122", "131122", "131113", "122113",
"113113", "112213", "112222", "112312", "112321", "111421", "111331", "111322", "111232", "111223", "111133", "111124", "111214", "112114", "121114", "121123", "121132", "112132",
"112141" };
 
private static final int[] MICRO_VARIANTS = { 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 11, 14, 17, 20, 24, 28, 8, 11, 14, 17, 20, 23,
26, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 4, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 11, 13, 15, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, 8, 12, 14, 16, 18, 21,
26, 32, 38, 44, 50, 0, 0, 0, 7, 7, 7, 7, 15, 15, 24, 34, 57, 84, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294, 7, 45, 70, 99, 115, 133, 154, 180, 212, 250, 294 };
 
/* rows, columns, error codewords, k-offset */
/* MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F */
private static final int[] MICROCOEFFS = {
/* k = 7 */
76, 925, 537, 597, 784, 691, 437,
/* k = 8 */
237, 308, 436, 284, 646, 653, 428, 379,
/* k = 9 */
567, 527, 622, 257, 289, 362, 501, 441, 205,
/* k = 10 */
377, 457, 64, 244, 826, 841, 818, 691, 266, 612,
/* k = 11 */
462, 45, 565, 708, 825, 213, 15, 68, 327, 602, 904,
/* k = 12 */
597, 864, 757, 201, 646, 684, 347, 127, 388, 7, 69, 851,
/* k = 13 */
764, 713, 342, 384, 606, 583, 322, 592, 678, 204, 184, 394, 692,
/* k = 14 */
669, 677, 154, 187, 241, 286, 274, 354, 478, 915, 691, 833, 105, 215,
/* k = 15 */
460, 829, 476, 109, 904, 664, 230, 5, 80, 74, 550, 575, 147, 868, 642,
/* k = 16 */
274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65,
/* k = 18 */
279, 577, 315, 624, 37, 855, 275, 739, 120, 297, 312, 202, 560, 321, 233, 756, 760, 573,
/* k = 21 */
108, 519, 781, 534, 129, 425, 681, 553, 422, 716, 763, 693, 624, 610, 310, 691, 347, 165, 193, 259, 568,
/* k = 26 */
443, 284, 887, 544, 788, 93, 477, 760, 331, 608, 269, 121, 159, 830, 446, 893, 699, 245, 441, 454, 325, 858, 131, 847, 764, 169,
/* k = 32 */
361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410,
/* k = 38 */
234, 228, 438, 848, 133, 703, 529, 721, 788, 322, 280, 159, 738, 586, 388, 684, 445, 680, 245, 595, 614, 233, 812, 32, 284, 658, 745, 229, 95, 689, 920, 771, 554, 289, 231, 125, 117, 518,
/* k = 44 */
476, 36, 659, 848, 678, 64, 764, 840, 157, 915, 470, 876, 109, 25, 632, 405, 417, 436, 714, 60, 376, 97, 413, 706, 446, 21, 3, 773, 569, 267, 272, 213, 31, 560, 231, 758, 103, 271, 572,
436, 339, 730, 82, 285,
/* k = 50 */
923, 797, 576, 875, 156, 706, 63, 81, 257, 874, 411, 416, 778, 50, 205, 303, 188, 535, 909, 155, 637, 230, 534, 96, 575, 102, 264, 233, 919, 593, 865, 26, 579, 623, 766, 146, 10, 739, 246,
127, 71, 244, 211, 477, 920, 876, 427, 820, 718, 435 };
 
/*
* following is Left RAP, Centre RAP, Right RAP and Start Cluster from ISO/IEC 24728:2006 tables
* 10, 11 and 12
*/
private static final int[] RAP_TABLE = { 1, 8, 36, 19, 9, 25, 1, 1, 8, 36, 19, 9, 27, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 47, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 19, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 9, 8, 36, 19, 17, 33, 1, 9, 8, 36, 19, 17, 35, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, 43, 1, 7, 15, 25, 37,
33, 17, 37, 47, 49, 0, 3, 6, 0, 6, 0, 0, 0, 3, 6, 0, 6, 6, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0, 3, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0 };
 
private String binary_string;
private int ecc;
private LinearEncoding symbology = LinearEncoding.CODE_128;
private String general_field;
private GeneralFieldMode[] general_field_type;
private int cc_width;
private final int[][] pwr928 = new int[69][7];
private final int[] codeWords = new int[180];
private int codeWordCount;
private final int[] bitStr = new int[13];
private int[] inputData;
private CompositeMode cc_mode;
private String linearContent;
private CompositeMode userPreferredMode = CompositeMode.CC_A;
private int target_bitsize;
private int remainder;
private int linearWidth; // Width of Code 128 linear
 
public Composite() {
this.inputDataType = Symbol.DataType.GS1;
}
 
@Override
public void setDataType(final DataType dataType) {
if (dataType != Symbol.DataType.GS1) {
throw new IllegalArgumentException("Only GS1 data type is supported for GS1 Composite symbology.");
}
}
 
@Override
protected boolean gs1Supported() {
return true;
}
 
/**
* Set the type of linear component included in the composite symbol, this will determine how
* the lower part of the symbol is encoded.
*
* @param linearSymbology The symbology of the linear component
*/
public void setSymbology(final LinearEncoding linearSymbology) {
this.symbology = linearSymbology;
}
 
/**
* Returns the type of linear component included in the composite symbol.
*
* @return the type of linear component included in the composite symbol
*/
public LinearEncoding getSymbology() {
return this.symbology;
}
 
/**
* Set the data to be encoded in the linear component of the composite symbol.
*
* @param linearContent the linear data in GS1 format
*/
public void setLinearContent(final String linearContent) {
this.linearContent = linearContent;
}
 
/**
* Returns the data encoded in the linear component of the composite symbol.
*
* @return the data encoded in the linear component of the composite symbol
*/
public String getLinearContent() {
return this.linearContent;
}
 
/**
* Set the preferred encoding method for the 2D component of the composite symbol. This value
* may be ignored if the amount of data supplied is too big for the selected encoding. Mode CC-C
* can only be used with a Code 128 linear component.
*
* @param userMode Preferred mode
*/
public void setPreferredMode(final CompositeMode userMode) {
this.userPreferredMode = userMode;
}
 
/**
* Returns the preferred encoding method for the 2D component of the composite symbol.
*
* @return the preferred encoding method for the 2D component of the composite symbol
*/
public CompositeMode getPreferredMode() {
return this.userPreferredMode;
}
 
@Override
protected void encode() {
 
List<Rectangle2D.Double> linear_rect;
List<TextBox> linear_txt;
final List<Rectangle2D.Double> combine_rect = new ArrayList<>();
final List<TextBox> combine_txt = new ArrayList<>();
String linear_encodeInfo;
int linear_height;
int top_shift = 0; // 2D component x-coordinate shift
int bottom_shift = 0; // linear component x-coordinate shift
this.linearWidth = 0;
 
if (this.linearContent.isEmpty()) {
throw new OkapiException("No linear data set");
}
 
// Manage composite component encoding first
encodeComposite();
 
// Then encode linear component
switch (this.symbology) {
case UPCA:
final Upc upca = new Upc();
upca.setMode(Upc.Mode.UPCA);
upca.setLinkageFlag(true);
upca.setContent(this.linearContent);
linear_rect = upca.rectangles;
linear_txt = upca.texts;
linear_height = upca.symbol_height;
linear_encodeInfo = upca.getEncodeInfo();
bottom_shift = 6;
top_shift = 3;
break;
case UPCE:
final Upc upce = new Upc();
upce.setMode(Upc.Mode.UPCE);
upce.setLinkageFlag(true);
upce.setContent(this.linearContent);
linear_rect = upce.rectangles;
linear_txt = upce.texts;
linear_height = upce.symbol_height;
linear_encodeInfo = upce.getEncodeInfo();
bottom_shift = 6;
top_shift = 3;
break;
case EAN:
final Ean ean = new Ean();
if (eanCalculateVersion() == 8) {
ean.setMode(Ean.Mode.EAN8);
bottom_shift = 14;
} else {
ean.setMode(Ean.Mode.EAN13);
bottom_shift = 6;
top_shift = 3;
}
ean.setLinkageFlag(true);
ean.setContent(this.linearContent);
linear_rect = ean.rectangles;
linear_txt = ean.texts;
linear_height = ean.symbol_height;
linear_encodeInfo = ean.getEncodeInfo();
break;
case CODE_128:
final Code128 code128 = new Code128();
switch (this.cc_mode) {
case CC_A:
code128.setCca();
break;
case CC_B:
code128.setCcb();
break;
case CC_C:
code128.setCcc();
bottom_shift = 7;
break;
}
code128.setDataType(Symbol.DataType.GS1);
code128.setContent(this.linearContent);
this.linearWidth = code128.symbol_width;
linear_rect = code128.rectangles;
linear_txt = code128.texts;
linear_height = code128.symbol_height;
linear_encodeInfo = code128.getEncodeInfo();
break;
case DATABAR_14:
final DataBar14 dataBar14 = new DataBar14();
dataBar14.setLinkageFlag(true);
dataBar14.setMode(Mode.LINEAR);
dataBar14.setContent(this.linearContent);
linear_rect = dataBar14.rectangles;
linear_txt = dataBar14.texts;
linear_height = dataBar14.symbol_height;
linear_encodeInfo = dataBar14.getEncodeInfo();
bottom_shift = 4;
break;
case DATABAR_14_STACK_OMNI:
final DataBar14 dataBar14SO = new DataBar14();
dataBar14SO.setLinkageFlag(true);
dataBar14SO.setMode(Mode.OMNI);
dataBar14SO.setContent(this.linearContent);
linear_rect = dataBar14SO.rectangles;
linear_txt = dataBar14SO.texts;
linear_height = dataBar14SO.symbol_height;
linear_encodeInfo = dataBar14SO.getEncodeInfo();
top_shift = 1;
break;
case DATABAR_14_STACK:
final DataBar14 dataBar14S = new DataBar14();
dataBar14S.setLinkageFlag(true);
dataBar14S.setMode(Mode.STACKED);
dataBar14S.setContent(this.linearContent);
linear_rect = dataBar14S.rectangles;
linear_txt = dataBar14S.texts;
linear_height = dataBar14S.symbol_height;
linear_encodeInfo = dataBar14S.getEncodeInfo();
top_shift = 1;
break;
case DATABAR_LIMITED:
final DataBarLimited dataBarLimited = new DataBarLimited();
dataBarLimited.setLinkageFlag();
dataBarLimited.setContent(this.linearContent);
linear_rect = dataBarLimited.rectangles;
linear_txt = dataBarLimited.texts;
linear_height = dataBarLimited.symbol_height;
linear_encodeInfo = dataBarLimited.getEncodeInfo();
top_shift = 1;
bottom_shift = 10;
break;
case DATABAR_EXPANDED:
final DataBarExpanded dataBarExpanded = new DataBarExpanded();
dataBarExpanded.setLinkageFlag(true);
dataBarExpanded.setStacked(false);
dataBarExpanded.setContent(this.linearContent);
linear_rect = dataBarExpanded.rectangles;
linear_txt = dataBarExpanded.texts;
linear_height = dataBarExpanded.symbol_height;
linear_encodeInfo = dataBarExpanded.getEncodeInfo();
top_shift = 2;
break;
case DATABAR_EXPANDED_STACK:
final DataBarExpanded dataBarExpandedS = new DataBarExpanded();
dataBarExpandedS.setLinkageFlag(true);
dataBarExpandedS.setStacked(true);
dataBarExpandedS.setContent(this.linearContent);
linear_rect = dataBarExpandedS.rectangles;
linear_txt = dataBarExpandedS.texts;
linear_height = dataBarExpandedS.symbol_height;
linear_encodeInfo = dataBarExpandedS.getEncodeInfo();
top_shift = 2;
break;
default:
throw new OkapiException("Linear symbol not recognised");
}
 
if (this.cc_mode == CompositeMode.CC_C && this.symbology == LinearEncoding.CODE_128) {
/*
* Width of composite component depends on width of linear component, so recalculate.
*/
this.row_count = 0;
this.rectangles.clear();
this.symbol_height = 0;
this.symbol_width = 0;
this.encodeInfo.setLength(0);
encodeComposite();
}
 
if (this.cc_mode != CompositeMode.CC_C && this.symbology == LinearEncoding.CODE_128) {
if (this.linearWidth > this.symbol_width) {
top_shift = (this.linearWidth - this.symbol_width) / 2;
}
}
 
for (final Rectangle2D.Double orig : this.rectangles) {
combine_rect.add(new Rectangle2D.Double(orig.x + top_shift, orig.y, orig.width, orig.height));
}
 
for (final Rectangle2D.Double orig : linear_rect) {
combine_rect.add(new Rectangle2D.Double(orig.x + bottom_shift, orig.y + this.symbol_height, orig.width, orig.height));
}
 
int max_x = 0;
for (final Rectangle2D.Double rect : combine_rect) {
if (rect.x + rect.width > max_x) {
max_x = (int) Math.ceil(rect.x + rect.width);
}
}
 
for (final TextBox orig : linear_txt) {
combine_txt.add(new TextBox(orig.x + bottom_shift, orig.y + this.symbol_height, orig.width, orig.text, this.humanReadableAlignment));
}
 
this.rectangles = combine_rect;
this.texts = combine_txt;
this.symbol_height += linear_height;
this.symbol_width = max_x;
info(linear_encodeInfo);
}
 
private void encodeComposite() {
 
if (this.content.length() > 2990) {
throw new OkapiException("2D component input data too long");
}
 
this.cc_mode = this.userPreferredMode;
 
if (this.cc_mode == CompositeMode.CC_C && this.symbology != LinearEncoding.CODE_128) {
/* CC-C can only be used with a GS1-128 linear part */
throw new OkapiException("Invalid mode (CC-C only valid with GS1-128 linear component)");
}
 
switch (this.symbology) {
/* Determine width of 2D component according to ISO/IEC 24723 Table 1 */
case EAN:
if (eanCalculateVersion() == 8) {
this.cc_width = 3;
} else {
this.cc_width = 4;
}
break;
case UPCE:
case DATABAR_14_STACK_OMNI:
case DATABAR_14_STACK:
this.cc_width = 2;
break;
case DATABAR_LIMITED:
this.cc_width = 3;
break;
case CODE_128:
case DATABAR_14:
case DATABAR_EXPANDED:
case UPCA:
case DATABAR_EXPANDED_STACK:
this.cc_width = 4;
break;
}
 
infoLine("Composite Width: " + this.cc_width);
 
if (this.cc_mode == CompositeMode.CC_A && !cc_binary_string()) {
this.cc_mode = CompositeMode.CC_B;
}
 
if (this.cc_mode == CompositeMode.CC_B) { /*
* If the data didn't fit into CC-A it is
* recalculated for CC-B
*/
if (!cc_binary_string()) {
if (this.symbology != LinearEncoding.CODE_128) {
throw new OkapiException("Input too long");
} else {
this.cc_mode = CompositeMode.CC_C;
}
}
}
 
if (this.cc_mode == CompositeMode.CC_C) {
/*
* If the data didn't fit in CC-B (and linear part is GS1-128) it is recalculated for
* CC-C
*/
if (!cc_binary_string()) {
throw new OkapiException("Input too long");
}
}
 
switch (this.cc_mode) { /* Note that ecc_level is only relevant to CC-C */
case CC_A:
cc_a();
infoLine("Composite Type: CC-A");
break;
case CC_B:
cc_b();
infoLine("Composite Type: CC-B");
break;
case CC_C:
cc_c();
infoLine("Composite Type: CC-C");
break;
}
 
super.plotSymbol();
}
 
@Override
protected void plotSymbol() {
// empty
}
 
private int eanCalculateVersion() {
/* Determine if EAN-8 or EAN-13 is being used */
 
int length = 0;
int i;
boolean latch;
 
latch = true;
for (i = 0; i < this.linearContent.length(); i++) {
if (this.linearContent.charAt(i) >= '0' && this.linearContent.charAt(i) <= '9') {
if (latch) {
length++;
}
} else {
latch = false;
}
}
 
if (length <= 7) {
// EAN-8
return 8;
} else {
// EAN-13
return 13;
}
}
 
private boolean calculateSymbolSize() {
int i;
final int binary_length = this.binary_string.length();
if (this.cc_mode == CompositeMode.CC_A) {
/* CC-A 2D component - calculate remaining space */
switch (this.cc_width) {
case 2:
if (binary_length > 167) {
return false;
}
if (binary_length <= 167) {
this.target_bitsize = 167;
}
if (binary_length <= 138) {
this.target_bitsize = 138;
}
if (binary_length <= 118) {
this.target_bitsize = 118;
}
if (binary_length <= 108) {
this.target_bitsize = 108;
}
if (binary_length <= 88) {
this.target_bitsize = 88;
}
if (binary_length <= 78) {
this.target_bitsize = 78;
}
if (binary_length <= 59) {
this.target_bitsize = 59;
}
break;
case 3:
if (binary_length > 167) {
return false;
}
if (binary_length <= 167) {
this.target_bitsize = 167;
}
if (binary_length <= 138) {
this.target_bitsize = 138;
}
if (binary_length <= 118) {
this.target_bitsize = 118;
}
if (binary_length <= 98) {
this.target_bitsize = 98;
}
if (binary_length <= 78) {
this.target_bitsize = 78;
}
break;
case 4:
if (binary_length > 197) {
return false;
}
if (binary_length <= 197) {
this.target_bitsize = 197;
}
if (binary_length <= 167) {
this.target_bitsize = 167;
}
if (binary_length <= 138) {
this.target_bitsize = 138;
}
if (binary_length <= 108) {
this.target_bitsize = 108;
}
if (binary_length <= 78) {
this.target_bitsize = 78;
}
break;
}
}
 
if (this.cc_mode == CompositeMode.CC_B) {
/* CC-B 2D component - calculated from ISO/IEC 24728 Table 1 */
switch (this.cc_width) {
case 2:
if (binary_length > 336) {
return false;
}
if (binary_length <= 336) {
this.target_bitsize = 336;
}
if (binary_length <= 296) {
this.target_bitsize = 296;
}
if (binary_length <= 256) {
this.target_bitsize = 256;
}
if (binary_length <= 208) {
this.target_bitsize = 208;
}
if (binary_length <= 160) {
this.target_bitsize = 160;
}
if (binary_length <= 104) {
this.target_bitsize = 104;
}
if (binary_length <= 56) {
this.target_bitsize = 56;
}
break;
case 3:
if (binary_length > 768) {
return false;
}
if (binary_length <= 768) {
this.target_bitsize = 768;
}
if (binary_length <= 648) {
this.target_bitsize = 648;
}
if (binary_length <= 536) {
this.target_bitsize = 536;
}
if (binary_length <= 416) {
this.target_bitsize = 416;
}
if (binary_length <= 304) {
this.target_bitsize = 304;
}
if (binary_length <= 208) {
this.target_bitsize = 208;
}
if (binary_length <= 152) {
this.target_bitsize = 152;
}
if (binary_length <= 112) {
this.target_bitsize = 112;
}
if (binary_length <= 72) {
this.target_bitsize = 72;
}
if (binary_length <= 32) {
this.target_bitsize = 32;
}
break;
case 4:
if (binary_length > 1184) {
return false;
}
if (binary_length <= 1184) {
this.target_bitsize = 1184;
}
if (binary_length <= 1016) {
this.target_bitsize = 1016;
}
if (binary_length <= 840) {
this.target_bitsize = 840;
}
if (binary_length <= 672) {
this.target_bitsize = 672;
}
if (binary_length <= 496) {
this.target_bitsize = 496;
}
if (binary_length <= 352) {
this.target_bitsize = 352;
}
if (binary_length <= 264) {
this.target_bitsize = 264;
}
if (binary_length <= 208) {
this.target_bitsize = 208;
}
if (binary_length <= 152) {
this.target_bitsize = 152;
}
if (binary_length <= 96) {
this.target_bitsize = 96;
}
if (binary_length <= 56) {
this.target_bitsize = 56;
}
break;
}
}
 
if (this.cc_mode == CompositeMode.CC_C) {
/* CC-C 2D Component is a bit more complex! */
int byte_length, codewords_used, ecc_level, ecc_codewords, rows;
int codewords_total, target_codewords, target_bytesize;
 
byte_length = binary_length / 8;
if (binary_length % 8 != 0) {
byte_length++;
}
 
codewords_used = byte_length / 6 * 5;
codewords_used += byte_length % 6;
 
ecc_level = 7;
if (codewords_used <= 1280) {
ecc_level = 6;
}
if (codewords_used <= 640) {
ecc_level = 5;
}
if (codewords_used <= 320) {
ecc_level = 4;
}
if (codewords_used <= 160) {
ecc_level = 3;
}
if (codewords_used <= 40) {
ecc_level = 2;
}
this.ecc = ecc_level;
ecc_codewords = 1;
for (i = 1; i <= ecc_level + 1; i++) {
ecc_codewords *= 2;
}
 
codewords_used += ecc_codewords;
codewords_used += 3;
 
if (this.linearWidth == 0) {
/* Linear component not yet calculated */
this.cc_width = (int) (0.5 + Math.sqrt(codewords_used / 3.0));
} else {
this.cc_width = (this.linearWidth - 53) / 17;
}
 
if (codewords_used / this.cc_width > 90) {
/* stop the symbol from becoming too high */
this.cc_width = this.cc_width + 1;
}
 
rows = codewords_used / this.cc_width;
if (codewords_used % this.cc_width != 0) {
rows++;
}
 
while (this.cc_width > 3 * rows) {
/* stop the symbol from becoming too wide (section 10) */
this.cc_width--;
 
rows = codewords_used / this.cc_width;
if (codewords_used % this.cc_width != 0) {
rows++;
}
}
 
codewords_total = this.cc_width * rows;
 
target_codewords = codewords_total - ecc_codewords;
target_codewords -= 3;
 
target_bytesize = 6 * (target_codewords / 5);
target_bytesize += target_codewords % 5;
 
this.target_bitsize = 8 * target_bytesize;
}
 
this.remainder = this.target_bitsize - binary_length;
return true;
}
 
private boolean cc_binary_string() {
/* Handles all data encodation from section 5 of ISO/IEC 24723 */
int encoding_method, read_posn, d1, d2, value, alpha_pad;
int i, j, ai_crop, fnc1_latch;
int group_val;
int ai90_mode;
boolean latch;
int alpha, alphanum, numeric, test1, test2, test3, next_ai_posn;
int numeric_value, table3_letter;
String numeric_part;
String ninety;
int latchOffset;
 
encoding_method = 1;
read_posn = 0;
ai_crop = 0;
fnc1_latch = 0;
alpha_pad = 0;
ai90_mode = 0;
this.ecc = 0;
value = 0;
this.target_bitsize = 0;
 
if (this.content.charAt(0) == '1' && (this.content.charAt(1) == '0' || this.content.charAt(1) == '1' || this.content.charAt(1) == '7') && this.content.length() >= 8) {
/* Source starts (10), (11) or (17) */
encoding_method = 2;
}
 
if (this.content.charAt(0) == '9' && this.content.charAt(1) == '0') {
/* Source starts (90) */
encoding_method = 3;
}
 
info("Composite Encodation: ");
switch (encoding_method) {
case 1:
infoLine("0");
break;
case 2:
infoLine("10");
break;
case 3:
infoLine("11");
break;
}
 
this.binary_string = "";
 
if (encoding_method == 1) {
this.binary_string += "0";
}
 
if (encoding_method == 2) {
/* Encoding Method field "10" - date and lot number */
 
this.binary_string += "10";
 
if (this.content.charAt(1) == '0') {
/* No date data */
this.binary_string += "11";
read_posn = 2;
} else {
/* Production Date (11) or Expiration Date (17) */
group_val = (10 * (this.content.charAt(2) - '0') + this.content.charAt(3) - '0') * 384;
group_val += (10 * (this.content.charAt(4) - '0') + this.content.charAt(5) - '0' - 1) * 32;
group_val += 10 * (this.content.charAt(6) - '0') + this.content.charAt(7) - '0';
 
for (j = 0; j < 16; j++) {
if ((group_val & 0x8000 >> j) == 0) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
 
if (this.content.charAt(1) == '1') {
/* Production Date AI 11 */
this.binary_string += "0";
} else {
/* Expiration Date AI 17 */
this.binary_string += "1";
}
read_posn = 8;
}
 
if (read_posn + 2 < this.content.length()) {
if (this.content.charAt(read_posn) == '1' && this.content.charAt(read_posn + 1) == '0') {
/* Followed by AI 10 - strip this from general field */
read_posn += 2;
} else {
/* An FNC1 character needs to be inserted in the general field */
fnc1_latch = 1;
}
} else {
fnc1_latch = 1;
}
}
 
if (encoding_method == 3) {
/* Encodation Method field of "11" - AI 90 */
/*
* "This encodation method may be used if an element string with an AI 90 occurs at the
* start of the data message, and if the data field following the two-digit AI 90 starts
* with an alphanumeric string which complies with a specific format." (para 5.2.2)
*/
 
j = this.content.length();
for (i = this.content.length(); i > 2; i--) {
if (this.content.charAt(i - 1) == '[') {
j = i;
}
}
 
ninety = this.content.substring(2, j - 1);
 
/* Find out if the AI 90 data is alphabetic or numeric or both */
 
alpha = 0;
alphanum = 0;
numeric = 0;
 
for (i = 0; i < ninety.length(); i++) {
 
if (ninety.charAt(i) >= 'A' && ninety.charAt(i) <= 'Z') {
/* Character is alphabetic */
alpha += 1;
}
 
if (ninety.charAt(i) >= '0' && ninety.charAt(i) <= '9') {
/* Character is numeric */
numeric += 1;
}
 
switch (ninety.charAt(i)) {
case '*':
case ',':
case '-':
case '.':
case '/':
alphanum += 1;
break;
}
 
if (!(ninety.charAt(i) >= '0' && ninety.charAt(i) <= '9' || ninety.charAt(i) >= 'A' && ninety.charAt(i) <= 'Z')) {
if (ninety.charAt(i) != '*' && ninety.charAt(i) != ',' && ninety.charAt(i) != '-' && ninety.charAt(i) != '.' && ninety.charAt(i) != '/') {
/* An Invalid AI 90 character */
throw new OkapiException("Invalid AI 90 data");
}
}
}
 
/* must start with 0, 1, 2 or 3 digits followed by an uppercase character */
test1 = -1;
for (i = 3; i >= 0; i--) {
if (ninety.charAt(i) >= 'A' && ninety.charAt(i) <= 'Z') {
test1 = i;
}
}
 
test2 = 0;
for (i = 0; i < test1; i++) {
if (!(ninety.charAt(i) >= '0' && ninety.charAt(i) <= '9')) {
test2 = 1;
}
}
 
/* leading zeros are not permitted */
test3 = 0;
if (test1 >= 1 && ninety.charAt(0) == '0') {
test3 = 1;
}
 
if (test1 != -1 && test2 != 1 && test3 == 0) {
/* Encodation method "11" can be used */
this.binary_string += "11";
 
numeric -= test1;
alpha--;
 
/* Decide on numeric, alpha or alphanumeric mode */
/* Alpha mode is a special mode for AI 90 */
 
if (alphanum > 0) {
/* Alphanumeric mode */
this.binary_string += "0";
ai90_mode = 1;
} else {
if (alpha > numeric) {
/* Alphabetic mode */
this.binary_string += "11";
ai90_mode = 2;
} else {
/* Numeric mode */
this.binary_string += "10";
ai90_mode = 3;
}
}
 
next_ai_posn = 2 + ninety.length();
 
if (this.content.charAt(next_ai_posn) == '[') {
/* There are more AIs afterwords */
if (this.content.charAt(next_ai_posn + 1) == '2' && this.content.charAt(next_ai_posn + 2) == '1') {
/* AI 21 follows */
ai_crop = 1;
}
 
if (this.content.charAt(next_ai_posn + 1) == '8' && this.content.charAt(next_ai_posn + 2) == '0' && this.content.charAt(next_ai_posn + 3) == '0'
&& this.content.charAt(next_ai_posn + 4) == '4') {
/* AI 8004 follows */
ai_crop = 2;
}
}
 
switch (ai_crop) {
case 0:
this.binary_string += "0";
break;
case 1:
this.binary_string += "10";
break;
case 2:
this.binary_string += "11";
break;
}
 
if (test1 == 0) {
numeric_part = "0";
} else {
numeric_part = ninety.substring(0, test1);
}
 
numeric_value = 0;
for (i = 0; i < numeric_part.length(); i++) {
numeric_value *= 10;
numeric_value += numeric_part.charAt(i) - '0';
}
 
table3_letter = -1;
if (numeric_value < 31) {
switch (ninety.charAt(test1)) {
case 'B':
table3_letter = 0;
break;
case 'D':
table3_letter = 1;
break;
case 'H':
table3_letter = 2;
break;
case 'I':
table3_letter = 3;
break;
case 'J':
table3_letter = 4;
break;
case 'K':
table3_letter = 5;
break;
case 'L':
table3_letter = 6;
break;
case 'N':
table3_letter = 7;
break;
case 'P':
table3_letter = 8;
break;
case 'Q':
table3_letter = 9;
break;
case 'R':
table3_letter = 10;
break;
case 'S':
table3_letter = 11;
break;
case 'T':
table3_letter = 12;
break;
case 'V':
table3_letter = 13;
break;
case 'W':
table3_letter = 14;
break;
case 'Z':
table3_letter = 15;
break;
}
}
 
if (table3_letter != -1) {
/* Encoding can be done according to 5.2.2 c) 2) */
/* five bit binary string representing value before letter */
for (j = 0; j < 5; j++) {
if ((numeric_value & 0x10 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
 
/* followed by four bit representation of letter from Table 3 */
for (j = 0; j < 4; j++) {
if ((table3_letter & 0x08 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
} else {
/* Encoding is done according to 5.2.2 c) 3) */
this.binary_string += "11111";
/* ten bit representation of number */
for (j = 0; j < 10; j++) {
if ((numeric_value & 0x200 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
 
/* five bit representation of ASCII character */
for (j = 0; j < 5; j++) {
if ((ninety.charAt(test1) - 65 & 0x10 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
read_posn = test1 + 3;
} else {
/* Use general field encodation instead */
this.binary_string += "0";
read_posn = 0;
}
 
/* Now encode the rest of the AI 90 data field */
if (ai90_mode == 2) {
/* Alpha encodation (section 5.2.3) */
do {
if (this.content.charAt(read_posn) >= '0' && this.content.charAt(read_posn) <= '9') {
for (j = 0; j < 5; j++) {
if ((this.content.charAt(read_posn) + 4 & 0x10 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.content.charAt(read_posn) >= 'A' && this.content.charAt(read_posn) <= 'Z') {
for (j = 0; j < 6; j++) {
if ((this.content.charAt(read_posn) - 65 & 0x20 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.content.charAt(read_posn) == '[') {
this.binary_string += "11111";
}
 
read_posn++;
} while (this.content.charAt(read_posn - 1) != '[' && read_posn < this.content.length());
alpha_pad = 1; /* This is overwritten if a general field is encoded */
}
 
if (ai90_mode == 1) {
/* Alphanumeric mode */
do {
if (this.content.charAt(read_posn) >= '0' && this.content.charAt(read_posn) <= '9') {
for (j = 0; j < 5; j++) {
if ((this.content.charAt(read_posn) - 43 & 0x10 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.content.charAt(read_posn) >= 'A' && this.content.charAt(read_posn) <= 'Z') {
for (j = 0; j < 6; j++) {
if ((this.content.charAt(read_posn) - 33 & 0x20 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
switch (this.content.charAt(read_posn)) {
case '[':
this.binary_string += "01111";
break;
case '*':
this.binary_string += "111010";
break;
case ',':
this.binary_string += "111011";
break;
case '-':
this.binary_string += "111100";
break;
case '.':
this.binary_string += "111101";
break;
case '/':
this.binary_string += "111110";
break;
}
 
read_posn++;
} while (this.content.charAt(read_posn - 1) != '[' && this.content.charAt(read_posn - 1) != '\0');
}
 
read_posn += 2 * ai_crop;
}
 
/*
* The compressed data field has been processed if appropriate - the rest of the data (if
* any) goes into a general-purpose data compaction field
*/
 
j = 0;
this.general_field = "";
if (fnc1_latch == 1) {
/*
* Encodation method "10" has been used but it is not followed by AI 10, so a FNC1
* character needs to be added
*/
this.general_field += "[";
}
 
this.general_field += this.content.substring(read_posn);
 
latch = false;
if (this.general_field.length() != 0) {
alpha_pad = 0;
 
this.general_field_type = new GeneralFieldMode[this.general_field.length()];
 
for (i = 0; i < this.general_field.length(); i++) {
/* Table 13 - ISO/IEC 646 encodation */
if (this.general_field.charAt(i) < ' ' || this.general_field.charAt(i) > 'z') {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
} else {
this.general_field_type[i] = GeneralFieldMode.ISOIEC;
}
 
if (this.general_field.charAt(i) == '#') {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (this.general_field.charAt(i) == '$') {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (this.general_field.charAt(i) == '@') {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (this.general_field.charAt(i) == 92) {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (this.general_field.charAt(i) == '^') {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
if (this.general_field.charAt(i) == 96) {
this.general_field_type[i] = GeneralFieldMode.INVALID_CHAR;
latch = true;
}
 
/* Table 12 - Alphanumeric encodation */
if (this.general_field.charAt(i) >= 'A' && this.general_field.charAt(i) <= 'Z') {
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (this.general_field.charAt(i) == '*') {
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (this.general_field.charAt(i) == ',') {
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (this.general_field.charAt(i) == '-') {
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (this.general_field.charAt(i) == '.') {
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
if (this.general_field.charAt(i) == '/') {
this.general_field_type[i] = GeneralFieldMode.ALPHA_OR_ISO;
}
 
/* Numeric encodation */
if (this.general_field.charAt(i) >= '0' && this.general_field.charAt(i) <= '9') {
this.general_field_type[i] = GeneralFieldMode.ANY_ENC;
}
if (this.general_field.charAt(i) == '[') {
/* FNC1 can be encoded in any system */
this.general_field_type[i] = GeneralFieldMode.ANY_ENC;
}
 
}
 
if (latch) {
/* Invalid characters in input data */
throw new OkapiException("Invalid characters in input data");
}
 
for (i = 0; i < this.general_field.length() - 1; i++) {
if (this.general_field_type[i] == GeneralFieldMode.ISOIEC && this.general_field.charAt(i + 1) == '[') {
this.general_field_type[i + 1] = GeneralFieldMode.ISOIEC;
}
}
 
for (i = 0; i < this.general_field.length() - 1; i++) {
if (this.general_field_type[i] == GeneralFieldMode.ALPHA_OR_ISO && this.general_field.charAt(i + 1) == '[') {
this.general_field_type[i + 1] = GeneralFieldMode.ALPHA_OR_ISO;
}
}
 
latch = applyGeneralFieldRules();
 
i = 0;
do {
switch (this.general_field_type[i]) {
case NUMERIC:
if (i != 0) {
if (this.general_field_type[i - 1] != GeneralFieldMode.NUMERIC && this.general_field.charAt(i - 1) != '[') {
this.binary_string += "000"; /* Numeric latch */
}
}
 
if (this.general_field.charAt(i) != '[') {
d1 = this.general_field.charAt(i) - '0';
} else {
d1 = 10;
}
 
if (i < this.general_field.length() - 1) {
if (this.general_field.charAt(i + 1) != '[') {
d2 = this.general_field.charAt(i + 1) - '0';
} else {
d2 = 10;
}
} else {
d2 = 10;
}
 
if (d1 != 10 || d2 != 10) {
/* If (d1==10)&&(d2==10) then input is either FNC1,FNC1 or FNC1,EOL */
value = 11 * d1 + d2 + 8;
 
for (j = 0; j < 7; j++) {
if ((value & 0x40 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
 
i += 2;
}
break;
 
case ALPHA:
if (i != 0) {
if (this.general_field_type[i - 1] == GeneralFieldMode.NUMERIC || this.general_field.charAt(i - 1) == '[') {
this.binary_string += "0000"; /* Alphanumeric latch */
}
if (this.general_field_type[i - 1] == GeneralFieldMode.ISOIEC) {
this.binary_string += "00100"; /* ISO/IEC 646 latch */
}
}
 
if (this.general_field.charAt(i) >= '0' && this.general_field.charAt(i) <= '9') {
 
value = this.general_field.charAt(i) - 43;
 
for (j = 0; j < 5; j++) {
if ((value & 0x10 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.general_field.charAt(i) >= 'A' && this.general_field.charAt(i) <= 'Z') {
 
value = this.general_field.charAt(i) - 33;
 
for (j = 0; j < 6; j++) {
if ((value & 0x20 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.general_field.charAt(i) == '[') {
this.binary_string += "01111"; /* FNC1/Numeric latch */
}
if (this.general_field.charAt(i) == '*') {
this.binary_string += "111010"; /* asterisk */
}
if (this.general_field.charAt(i) == ',') {
this.binary_string += "111011"; /* comma */
}
if (this.general_field.charAt(i) == '-') {
this.binary_string += "111100"; /* minus or hyphen */
}
if (this.general_field.charAt(i) == '.') {
this.binary_string += "111101"; /* period or full stop */
}
if (this.general_field.charAt(i) == '/') {
this.binary_string += "111110"; /* slash or solidus */
}
 
i++;
break;
 
case ISOIEC:
if (i != 0) {
if (this.general_field_type[i - 1] == GeneralFieldMode.NUMERIC || this.general_field.charAt(i - 1) == '[') {
this.binary_string += "0000"; /* Alphanumeric latch */
this.binary_string += "00100"; /* ISO/IEC 646 latch */
}
if (this.general_field_type[i - 1] == GeneralFieldMode.ALPHA) {
this.binary_string += "00100"; /* ISO/IEC 646 latch */
}
}
 
if (this.general_field.charAt(i) >= '0' && this.general_field.charAt(i) <= '9') {
 
value = this.general_field.charAt(i) - 43;
 
for (j = 0; j < 5; j++) {
if ((value & 0x10 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.general_field.charAt(i) >= 'A' && this.general_field.charAt(i) <= 'Z') {
 
value = this.general_field.charAt(i) - 1;
 
for (j = 0; j < 7; j++) {
if ((value & 0x40 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.general_field.charAt(i) >= 'a' && this.general_field.charAt(i) <= 'z') {
 
value = this.general_field.charAt(i) - 7;
 
for (j = 0; j < 7; j++) {
if ((value & 0x40 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
}
 
if (this.general_field.charAt(i) == '[') {
this.binary_string += "01111"; /* FNC1/Numeric latch */
}
if (this.general_field.charAt(i) == '!') {
this.binary_string += "11101000"; /* exclamation mark */
}
if (this.general_field.charAt(i) == 34) {
this.binary_string += "11101001"; /* quotation mark */
}
if (this.general_field.charAt(i) == 37) {
this.binary_string += "11101010"; /* percent sign */
}
if (this.general_field.charAt(i) == '&') {
this.binary_string += "11101011"; /* ampersand */
}
if (this.general_field.charAt(i) == 39) {
this.binary_string += "11101100"; /* apostrophe */
}
if (this.general_field.charAt(i) == '(') {
this.binary_string += "11101101"; /* left parenthesis */
}
if (this.general_field.charAt(i) == ')') {
this.binary_string += "11101110"; /* right parenthesis */
}
if (this.general_field.charAt(i) == '*') {
this.binary_string += "11101111"; /* asterisk */
}
if (this.general_field.charAt(i) == '+') {
this.binary_string += "11110000"; /* plus sign */
}
if (this.general_field.charAt(i) == ',') {
this.binary_string += "11110001"; /* comma */
}
if (this.general_field.charAt(i) == '-') {
this.binary_string += "11110010"; /* minus or hyphen */
}
if (this.general_field.charAt(i) == '.') {
this.binary_string += "11110011"; /* period or full stop */
}
if (this.general_field.charAt(i) == '/') {
this.binary_string += "11110100"; /* slash or solidus */
}
if (this.general_field.charAt(i) == ':') {
this.binary_string += "11110101"; /* colon */
}
if (this.general_field.charAt(i) == ';') {
this.binary_string += "11110110"; /* semicolon */
}
if (this.general_field.charAt(i) == '<') {
this.binary_string += "11110111"; /* less-than sign */
}
if (this.general_field.charAt(i) == '=') {
this.binary_string += "11111000"; /* equals sign */
}
if (this.general_field.charAt(i) == '>') {
this.binary_string += "11111001"; /* greater-than sign */
}
if (this.general_field.charAt(i) == '?') {
this.binary_string += "11111010"; /* question mark */
}
if (this.general_field.charAt(i) == '_') {
this.binary_string += "11111011"; /* underline or low line */
}
if (this.general_field.charAt(i) == ' ') {
this.binary_string += "11111100"; /* space */
}
 
i++;
break;
}
 
latchOffset = 0;
if (latch) {
latchOffset = 1;
}
} while (i + latchOffset < this.general_field.length());
}
 
if (!calculateSymbolSize()) {
return false;
}
 
if (latch) {
i = this.general_field.length() - 1;
/* There is still one more numeric digit to encode */
 
if (this.general_field.charAt(i) == '[') {
this.binary_string += "000001111";
} else {
if (this.remainder >= 4 && this.remainder <= 6) {
d1 = this.general_field.charAt(i) - '0';
d1++;
 
for (j = 0; j < 4; j++) {
if ((value & 0x08 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
} else {
d1 = this.general_field.charAt(i) - '0';
d2 = 10;
 
value = 11 * d1 + d2 + 8;
 
for (j = 0; j < 7; j++) {
if ((value & 0x40 >> j) == 0x00) {
this.binary_string += "0";
} else {
this.binary_string += "1";
}
}
/* This may push the symbol up to the next size */
}
}
}
 
if (this.binary_string.length() > 11805) { /* (2361 * 5) */
throw new OkapiException("Input too long");
}
 
/* size of the symbol may have changed when adding data in the above sequence */
if (!calculateSymbolSize()) {
return false;
}
 
infoLine("Composite Binary Length: " + this.binary_string.length());
displayBinaryString();
 
if (this.binary_string.length() < this.target_bitsize) {
/* Now add padding to binary string */
if (alpha_pad == 1) {
this.binary_string += "11111";
alpha_pad = 0;
/* Extra FNC1 character required after Alpha encodation (section 5.2.3) */
}
 
if (this.general_field.length() != 0 && this.general_field_type[this.general_field.length() - 1] == GeneralFieldMode.NUMERIC) {
this.binary_string += "0000";
}
 
while (this.binary_string.length() < this.target_bitsize) {
this.binary_string += "00100";
}
 
this.binary_string = this.binary_string.substring(0, this.target_bitsize);
}
 
return true;
}
 
private void displayBinaryString() {
int i, nibble;
/* Display binary string as hexadecimal */
 
info("Composite Binary String: ");
nibble = 0;
for (i = 0; i < this.binary_string.length(); i++) {
switch (i % 4) {
case 0:
if (this.binary_string.charAt(i) == '1') {
nibble += 8;
}
break;
case 1:
if (this.binary_string.charAt(i) == '1') {
nibble += 4;
}
break;
case 2:
if (this.binary_string.charAt(i) == '1') {
nibble += 2;
}
break;
case 3:
if (this.binary_string.charAt(i) == '1') {
nibble += 1;
}
info(Integer.toHexString(nibble));
nibble = 0;
break;
}
}
 
if (this.binary_string.length() % 4 != 0) {
info(Integer.toHexString(nibble));
}
infoLine();
}
 
private boolean applyGeneralFieldRules() {
/*
* Attempts to apply encoding rules from secions 7.2.5.5.1 to 7.2.5.5.3 of ISO/IEC
* 24724:2006
*/
 
int block_count, i, j, k;
GeneralFieldMode current, next, last;
final int[] blockLength = new int[200];
final GeneralFieldMode[] blockType = new GeneralFieldMode[200];
 
block_count = 0;
 
blockLength[block_count] = 1;
blockType[block_count] = this.general_field_type[0];
 
for (i = 1; i < this.general_field.length(); i++) {
current = this.general_field_type[i];
last = this.general_field_type[i - 1];
 
if (current == last) {
blockLength[block_count] = blockLength[block_count] + 1;
} else {
block_count++;
blockLength[block_count] = 1;
blockType[block_count] = this.general_field_type[i];
}
}
 
block_count++;
 
for (i = 0; i < block_count; i++) {
current = blockType[i];
next = blockType[i + 1];
 
if (current == GeneralFieldMode.ISOIEC && i != block_count - 1) {
if (next == GeneralFieldMode.ANY_ENC && blockLength[i + 1] >= 4) {
blockType[i + 1] = GeneralFieldMode.NUMERIC;
}
if (next == GeneralFieldMode.ANY_ENC && blockLength[i + 1] < 4) {
blockType[i + 1] = GeneralFieldMode.ISOIEC;
}
if (next == GeneralFieldMode.ALPHA_OR_ISO && blockLength[i + 1] >= 5) {
blockType[i + 1] = GeneralFieldMode.ALPHA;
}
if (next == GeneralFieldMode.ALPHA_OR_ISO && blockLength[i + 1] < 5) {
blockType[i + 1] = GeneralFieldMode.ISOIEC;
}
}
 
if (current == GeneralFieldMode.ALPHA_OR_ISO) {
blockType[i] = GeneralFieldMode.ALPHA;
}
 
if (current == GeneralFieldMode.ALPHA && i != block_count - 1) {
if (next == GeneralFieldMode.ANY_ENC && blockLength[i + 1] >= 6) {
blockType[i + 1] = GeneralFieldMode.NUMERIC;
}
if (next == GeneralFieldMode.ANY_ENC && blockLength[i + 1] < 6) {
if (i == block_count - 2 && blockLength[i + 1] >= 4) {
blockType[i + 1] = GeneralFieldMode.NUMERIC;
} else {
blockType[i + 1] = GeneralFieldMode.ALPHA;
}
}
}
 
if (current == GeneralFieldMode.ANY_ENC) {
blockType[i] = GeneralFieldMode.NUMERIC;
}
}
 
if (block_count > 1) {
i = 1;
while (i < block_count) {
if (blockType[i - 1] == blockType[i]) {
/* bring together */
blockLength[i - 1] = blockLength[i - 1] + blockLength[i];
j = i + 1;
 
/* decreace the list */
while (j < block_count) {
blockLength[j - 1] = blockLength[j];
blockType[j - 1] = blockType[j];
j++;
}
block_count--;
i--;
}
i++;
}
}
 
for (i = 0; i < block_count - 1; i++) {
if (blockType[i] == GeneralFieldMode.NUMERIC && (blockLength[i] & 1) != 0) {
/* Odd size numeric block */
blockLength[i] = blockLength[i] - 1;
blockLength[i + 1] = blockLength[i + 1] + 1;
}
}
 
j = 0;
for (i = 0; i < block_count; i++) {
for (k = 0; k < blockLength[i]; k++) {
this.general_field_type[j] = blockType[i];
j++;
}
}
 
if (blockType[block_count - 1] == GeneralFieldMode.NUMERIC && (blockLength[block_count - 1] & 1) != 0) {
/*
* If the last block is numeric and an odd size, further processing needs to be done
* outside this procedure
*/
return true;
} else {
return false;
}
}
 
private void cc_a() {
/* CC-A 2D component */
int i, strpos, segment, cwCnt, variant, rows;
int k, offset, j, total;
final int[] rsCodeWords = new int[8];
int LeftRAPStart, RightRAPStart, CentreRAPStart, StartCluster;
int LeftRAP, RightRAP, CentreRAP, Cluster;
final int[] dummy = new int[5];
int flip, loop;
String codebarre;
final StringBuilder bin = new StringBuilder();
String local_source; /* A copy of source but with padding zeroes to make 208 bits */
 
variant = 0;
 
for (i = 0; i < 13; i++) {
this.bitStr[i] = 0;
}
for (i = 0; i < 28; i++) {
this.codeWords[i] = 0;
}
 
local_source = this.binary_string;
for (i = this.binary_string.length(); i < 208; i++) {
local_source += "0";
}
 
for (segment = 0; segment < 13; segment++) {
strpos = segment * 16;
this.bitStr[segment] = 0;
for (i = 0; i < 16; i++) {
if (local_source.charAt(strpos + i) == '1') {
this.bitStr[segment] += 0x8000 >> i;
}
}
}
 
init928();
/* encode codeWords from bitStr */
cwCnt = encode928(this.binary_string.length());
 
switch (this.cc_width) {
case 2:
switch (cwCnt) {
case 6:
variant = 0;
break;
case 8:
variant = 1;
break;
case 9:
variant = 2;
break;
case 11:
variant = 3;
break;
case 12:
variant = 4;
break;
case 14:
variant = 5;
break;
case 17:
variant = 6;
break;
}
break;
case 3:
switch (cwCnt) {
case 8:
variant = 7;
break;
case 10:
variant = 8;
break;
case 12:
variant = 9;
break;
case 14:
variant = 10;
break;
case 17:
variant = 11;
break;
}
break;
case 4:
switch (cwCnt) {
case 8:
variant = 12;
break;
case 11:
variant = 13;
break;
case 14:
variant = 14;
break;
case 17:
variant = 15;
break;
case 20:
variant = 16;
break;
}
break;
}
 
rows = CCA_VARIANTS[variant];
k = CCA_VARIANTS[17 + variant];
offset = CCA_VARIANTS[34 + variant];
 
/* Reed-Solomon error correction */
 
for (i = 0; i < 8; i++) {
rsCodeWords[i] = 0;
}
total = 0;
info("Composite Codewords: ");
for (i = 0; i < cwCnt; i++) {
total = (this.codeWords[i] + rsCodeWords[k - 1]) % 929;
for (j = k - 1; j >= 0; j--) {
if (j == 0) {
rsCodeWords[j] = (929 - total * CCA_COEFFS[offset + j] % 929) % 929;
} else {
rsCodeWords[j] = (rsCodeWords[j - 1] + 929 - total * CCA_COEFFS[offset + j] % 929) % 929;
}
}
infoSpace(this.codeWords[i]);
}
infoLine();
 
for (j = 0; j < k; j++) {
if (rsCodeWords[j] != 0) {
rsCodeWords[j] = 929 - rsCodeWords[j];
}
}
 
for (i = k - 1; i >= 0; i--) {
this.codeWords[cwCnt] = rsCodeWords[i];
cwCnt++;
}
 
/* Place data into table */
LeftRAPStart = A_RAP_TABLE[variant];
CentreRAPStart = A_RAP_TABLE[variant + 17];
RightRAPStart = A_RAP_TABLE[variant + 34];
StartCluster = A_RAP_TABLE[variant + 51] / 3;
 
LeftRAP = LeftRAPStart;
CentreRAP = CentreRAPStart;
RightRAP = RightRAPStart;
Cluster = StartCluster; /*
* Cluster can be 0, 1 or 2 for Cluster(0), Cluster(3) and
* Cluster(6)
*/
 
this.readable = "";
this.row_count = rows;
this.pattern = new String[this.row_count];
this.row_height = new int[this.row_count];
 
for (i = 0; i < rows; i++) {
codebarre = "";
offset = 929 * Cluster;
for (j = 0; j < 5; j++) {
dummy[j] = 0;
}
for (j = 0; j < this.cc_width; j++) {
dummy[j + 1] = this.codeWords[i * this.cc_width + j];
}
/* Copy the data into codebarre */
codebarre += RAPLR[LeftRAP];
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[1]];
codebarre += "1";
if (this.cc_width == 3) {
codebarre += RAPC[CentreRAP];
}
if (this.cc_width >= 2) {
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[2]];
codebarre += "1";
}
if (this.cc_width == 4) {
codebarre += RAPC[CentreRAP];
}
if (this.cc_width >= 3) {
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[3]];
codebarre += "1";
}
if (this.cc_width == 4) {
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[4]];
codebarre += "1";
}
codebarre += RAPLR[RightRAP];
codebarre += "1"; /* stop */
 
/* Now codebarre is a mixture of letters and numbers */
 
flip = 1;
bin.setLength(0);
for (loop = 0; loop < codebarre.length(); loop++) {
if (codebarre.charAt(loop) >= '0' && codebarre.charAt(loop) <= '9') {
for (k = 0; k < codebarre.charAt(loop) - '0'; k++) {
if (flip == 0) {
bin.append('0');
} else {
bin.append('1');
}
}
if (flip == 0) {
flip = 1;
} else {
flip = 0;
}
} else {
bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]);
}
}
 
this.row_height[i] = 2;
this.pattern[i] = bin2pat(bin);
 
/* Set up RAPs and Cluster for next row */
LeftRAP++;
CentreRAP++;
RightRAP++;
Cluster++;
 
if (LeftRAP == 53) {
LeftRAP = 1;
}
if (CentreRAP == 53) {
CentreRAP = 1;
}
if (RightRAP == 53) {
RightRAP = 1;
}
if (Cluster == 3) {
Cluster = 0;
}
}
}
 
/* initialize pwr928 encoding table */
private void init928() {
int i, j, v;
final int[] cw = new int[7];
cw[6] = 1;
for (i = 5; i >= 0; i--) {
cw[i] = 0;
}
 
for (i = 0; i < 7; i++) {
this.pwr928[0][i] = cw[i];
}
for (j = 1; j < 69; j++) {
for (v = 0, i = 6; i >= 1; i--) {
v = 2 * cw[i] + v / 928;
this.pwr928[j][i] = cw[i] = v % 928;
}
this.pwr928[j][0] = cw[0] = 2 * cw[0] + v / 928;
}
}
 
/* converts bit string to base 928 values, codeWords[0] is highest order */
private int encode928(final int bitLng) {
int i, j, b, bitCnt, cwNdx, cwCnt, cwLng;
for (cwNdx = cwLng = b = 0; b < bitLng; b += 69, cwNdx += 7) {
bitCnt = min(bitLng - b, 69);
cwLng += cwCnt = bitCnt / 10 + 1;
for (i = 0; i < cwCnt; i++) {
this.codeWords[cwNdx + i] = 0; /* init 0 */
}
for (i = 0; i < bitCnt; i++) {
if (getBit(b + bitCnt - i - 1)) {
for (j = 0; j < cwCnt; j++) {
this.codeWords[cwNdx + j] += this.pwr928[i][j + 7 - cwCnt];
}
}
}
for (i = cwCnt - 1; i > 0; i--) {
/* add "carries" */
this.codeWords[cwNdx + i - 1] += this.codeWords[cwNdx + i] / 928;
this.codeWords[cwNdx + i] %= 928;
}
}
return cwLng;
}
 
private int min(final int first, final int second) {
if (first <= second) {
return first;
} else {
return second;
}
}
 
/* gets bit in bitString at bitPos */
private boolean getBit(final int arg) {
if ((this.bitStr[arg >> 4] & 0x8000 >> (arg & 15)) != 0) {
return true;
} else {
return false;
}
}
 
private void cc_b() {
/* CC-B 2D component */
int length, i, binloc;
int k, j, longueur, offset;
final int[] mccorrection = new int[50];
int total;
final int[] dummy = new int[5];
String codebarre;
final StringBuilder bin = new StringBuilder();
int variant, LeftRAPStart, CentreRAPStart, RightRAPStart, StartCluster;
int LeftRAP, CentreRAP, RightRAP, Cluster, flip, loop;
int option_2, rows;
this.inputData = new int[this.binary_string.length() / 8 + 3];
 
length = this.binary_string.length() / 8;
 
for (i = 0; i < length; i++) {
binloc = i * 8;
 
this.inputData[i] = 0;
for (j = 0; j < 8; j++) {
if (this.binary_string.charAt(binloc + j) == '1') {
this.inputData[i] += 0x80 >> j;
}
}
}
 
this.codeWordCount = 0;
 
/*
* "the CC-B component shall have codeword 920 in the first symbol character position"
* (section 9a)
*/
this.codeWords[this.codeWordCount] = 920;
this.codeWordCount++;
 
byteprocess(0, length);
 
/* Now figure out which variant of the symbol to use and load values accordingly */
 
variant = 0;
 
if (this.cc_width == 2) {
variant = 13;
if (this.codeWordCount <= 33) {
variant = 12;
}
if (this.codeWordCount <= 29) {
variant = 11;
}
if (this.codeWordCount <= 24) {
variant = 10;
}
if (this.codeWordCount <= 19) {
variant = 9;
}
if (this.codeWordCount <= 13) {
variant = 8;
}
if (this.codeWordCount <= 8) {
variant = 7;
}
}
 
if (this.cc_width == 3) {
variant = 23;
if (this.codeWordCount <= 70) {
variant = 22;
}
if (this.codeWordCount <= 58) {
variant = 21;
}
if (this.codeWordCount <= 46) {
variant = 20;
}
if (this.codeWordCount <= 34) {
variant = 19;
}
if (this.codeWordCount <= 24) {
variant = 18;
}
if (this.codeWordCount <= 18) {
variant = 17;
}
if (this.codeWordCount <= 14) {
variant = 16;
}
if (this.codeWordCount <= 10) {
variant = 15;
}
if (this.codeWordCount <= 6) {
variant = 14;
}
}
 
if (this.cc_width == 4) {
variant = 34;
if (this.codeWordCount <= 108) {
variant = 33;
}
if (this.codeWordCount <= 90) {
variant = 32;
}
if (this.codeWordCount <= 72) {
variant = 31;
}
if (this.codeWordCount <= 54) {
variant = 30;
}
if (this.codeWordCount <= 39) {
variant = 29;
}
if (this.codeWordCount <= 30) {
variant = 28;
}
if (this.codeWordCount <= 24) {
variant = 27;
}
if (this.codeWordCount <= 18) {
variant = 26;
}
if (this.codeWordCount <= 12) {
variant = 25;
}
if (this.codeWordCount <= 8) {
variant = 24;
}
}
 
/*
* Now we have the variant we can load the data - from here on the same as MicroPDF417 code
*/
variant--;
option_2 = MICRO_VARIANTS[variant]; /* columns */
rows = MICRO_VARIANTS[variant + 34]; /* rows */
k = MICRO_VARIANTS[variant + 68]; /* number of EC CWs */
longueur = option_2 * rows - k; /* number of non-EC CWs */
i = longueur - this.codeWordCount; /* amount of padding required */
offset = MICRO_VARIANTS[variant + 102]; /* coefficient offset */
 
/* We add the padding */
while (i > 0) {
this.codeWords[this.codeWordCount] = 900;
this.codeWordCount++;
i--;
}
 
/* Reed-Solomon error correction */
longueur = this.codeWordCount;
for (loop = 0; loop < 50; loop++) {
mccorrection[loop] = 0;
}
total = 0;
info("Composite Codewords: ");
for (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 * MICROCOEFFS[offset + j] % 929) % 929;
} else {
mccorrection[j] = (mccorrection[j - 1] + 929 - total * MICROCOEFFS[offset + j] % 929) % 929;
}
}
infoSpace(this.codeWords[i]);
}
infoLine();
 
for (j = 0; j < k; j++) {
if (mccorrection[j] != 0) {
mccorrection[j] = 929 - mccorrection[j];
}
}
/* we add these codes to the string */
for (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.row_count = rows;
this.pattern = new String[this.row_count];
this.row_height = new int[this.row_count];
 
for (i = 0; i < rows; i++) {
codebarre = "";
offset = 929 * Cluster;
for (j = 0; j < 5; j++) {
dummy[j] = 0;
}
for (j = 0; j < option_2; j++) {
dummy[j + 1] = this.codeWords[i * option_2 + j];
}
/* Copy the data into codebarre */
codebarre += RAPLR[LeftRAP];
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[1]];
codebarre += "1";
if (this.cc_width == 3) {
codebarre += RAPC[CentreRAP];
}
if (this.cc_width >= 2) {
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[2]];
codebarre += "1";
}
if (this.cc_width == 4) {
codebarre += RAPC[CentreRAP];
}
if (this.cc_width >= 3) {
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[3]];
codebarre += "1";
}
if (this.cc_width == 4) {
codebarre += "1";
codebarre += CODAGEMC[offset + dummy[4]];
codebarre += "1";
}
codebarre += RAPLR[RightRAP];
codebarre += "1"; /* stop */
 
/* Now codebarre is a mixture of letters and numbers */
 
flip = 1;
bin.setLength(0);
for (loop = 0; loop < codebarre.length(); loop++) {
if (codebarre.charAt(loop) >= '0' && codebarre.charAt(loop) <= '9') {
for (k = 0; k < codebarre.charAt(loop) - '0'; k++) {
if (flip == 0) {
bin.append('0');
} else {
bin.append('1');
}
}
if (flip == 0) {
flip = 1;
} else {
flip = 0;
}
} else {
bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]);
}
}
 
this.pattern[i] = bin2pat(bin);
this.row_height[i] = 2;
 
/* 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 cc_c() {
/* CC-C 2D component - byte compressed PDF417 */
int length, i, binloc, k;
int offset, longueur, loop, total, j;
final int[] mccorrection = new int[520];
int c1, c2, c3;
final int[] dummy = new int[35];
String codebarre;
final StringBuilder bin = new StringBuilder();
this.inputData = new int[this.binary_string.length() / 8 + 4];
 
length = this.binary_string.length() / 8;
 
for (i = 0; i < length; i++) {
binloc = i * 8;
this.inputData[i] = 0;
for (j = 0; j < 8; j++) {
if (this.binary_string.charAt(binloc + j) == '1') {
this.inputData[i] += 0x80 >> j;
}
}
}
 
this.codeWordCount = 0;
this.codeWords[this.codeWordCount] = 0; /* space for length descriptor */
this.codeWordCount++;
this.codeWords[this.codeWordCount] = 920; /* CC-C identifier */
this.codeWordCount++;
 
byteprocess(0, length);
 
this.codeWords[0] = this.codeWordCount;
 
k = 1;
for (i = 1; i <= this.ecc + 1; i++) {
k *= 2;
}
 
/* 796 - we now take care of the Reed Solomon codes */
switch (this.ecc) {
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;
}
 
longueur = this.codeWordCount;
for (loop = 0; loop < 520; loop++) {
mccorrection[loop] = 0;
}
total = 0;
info("Composite Codewords: ");
for (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 * COEFRS[offset + j] % 929) % 929;
} else {
mccorrection[j] = (mccorrection[j - 1] + 929 - total * COEFRS[offset + j] % 929) % 929;
}
}
infoSpace(this.codeWords[i]);
}
infoLine();
 
for (j = 0; j < k; j++) {
if (mccorrection[j] != 0) {
mccorrection[j] = 929 - mccorrection[j];
}
}
/* we add these codes to the string */
for (i = k - 1; i >= 0; i--) {
this.codeWords[this.codeWordCount] = mccorrection[i];
this.codeWordCount++;
}
 
/* 818 - The CW string is finished */
c1 = (this.codeWordCount / this.cc_width - 1) / 3;
c2 = this.ecc * 3 + (this.codeWordCount / this.cc_width - 1) % 3;
c3 = this.cc_width - 1;
 
this.readable = "";
this.row_count = this.codeWordCount / this.cc_width;
this.pattern = new String[this.row_count];
this.row_height = new int[this.row_count];
 
/* we now encode each row */
for (i = 0; i <= this.codeWordCount / this.cc_width - 1; i++) {
for (j = 0; j < this.cc_width; j++) {
dummy[j + 1] = this.codeWords[i * this.cc_width + j];
}
k = i / 3 * 30;
switch (i % 3) {
/*
* follows this pattern from US Patent 5,243,655: Row 0: L0 (row #, # of rows) R0 (row
* #, # of columns) Row 1: L1 (row #, security level) R1 (row #, # of rows) Row 2: L2
* (row #, # of columns) R2 (row #, security level) Row 3: L3 (row #, # of rows) R3 (row
* #, # of columns) etc.
*/
case 0:
dummy[0] = k + c1;
dummy[this.cc_width + 1] = k + c3;
break;
case 1:
dummy[0] = k + c2;
dummy[this.cc_width + 1] = k + c1;
break;
case 2:
dummy[0] = k + c3;
dummy[this.cc_width + 1] = k + c2;
break;
}
codebarre = "+*"; /* Start with a start char and a separator */
 
for (j = 0; j <= this.cc_width + 1; j++) {
switch (i % 3) {
case 1:
offset = 929;
/* cluster(3) */ break;
case 2:
offset = 1858;
/* cluster(6) */ break;
default:
offset = 0;
/* cluster(0) */ break;
}
codebarre += CODAGEMC[offset + dummy[j]];
codebarre += "*";
}
codebarre += "-";
 
bin.setLength(0);
for (loop = 0; loop < codebarre.length(); loop++) {
bin.append(PDF_TTF[positionOf(codebarre.charAt(loop), BR_SET)]);
}
this.pattern[i] = bin2pat(bin);
this.row_height[i] = 3;
}
}
 
private void byteprocess(int start, final int length) {
int len = 0;
int chunkLen = 0;
BigInteger mantisa;
BigInteger total;
BigInteger word;
 
/* select the switch for multiple of 6 bytes */
if (this.binary_string.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++];
}
}
}
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/Pharmacode.java
New file
0,0 → 1,77
/*
* 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;
 
/**
* Implements the <a href="http://en.wikipedia.org/wiki/Pharmacode">Pharmacode</a> bar code
* symbology. <br>
* Pharmacode is used for the identification of pharmaceuticals. The symbology is able to encode
* whole numbers between 3 and 131070.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class Pharmacode extends Symbol {
 
@Override
protected void encode() {
int tester = 0;
int i;
 
String inter = "";
String dest = "";
 
if (this.content.length() > 6) {
throw new OkapiException("Input too long");
}
 
if (!this.content.matches("[0-9]+")) {
throw new OkapiException("Invalid characters in data");
}
 
for (i = 0; i < this.content.length(); i++) {
tester *= 10;
tester += Character.getNumericValue(this.content.charAt(i));
}
 
if (tester < 3 || tester > 131070) {
throw new OkapiException("Data out of range");
}
 
do {
if ((tester & 1) == 0) {
inter += "W";
tester = (tester - 2) / 2;
} else {
inter += "N";
tester = (tester - 1) / 2;
}
} while (tester != 0);
 
for (i = inter.length() - 1; i >= 0; i--) {
if (inter.charAt(i) == 'W') {
dest += "32";
} else {
dest += "12";
}
}
 
this.readable = "";
this.pattern = new String[1];
this.pattern[0] = dest;
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
}
}
/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/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/Code128.java
New file
0,0 → 1,865
/*
* 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 java.nio.charset.StandardCharsets.ISO_8859_1;
 
/**
* <p>
* Implements Code 128 bar code symbology according to ISO/IEC 15417:2007.
*
* <p>
* Code 128 supports encoding of 8-bit ISO 8859-1 (Latin-1) characters.
*
* <p>
* Setting GS1 mode allows encoding in GS1-128 (also known as UCC/EAN-128).
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @author Daniel Gredler
*/
public class Code128 extends Symbol {
 
private enum Mode {
NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC
}
 
private enum FMode {
SHIFTN, LATCHN, SHIFTF, LATCHF
}
 
private enum Composite {
OFF, CCA, CCB, CCC
}
 
protected static final String[] CODE128_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 boolean suppressModeC = false;
private Composite compositeMode = Composite.OFF;
 
/**
* Optionally prevents this symbol from using subset mode C for numeric data compression.
*
* @param suppressModeC whether or not to prevent this symbol from using subset mode C
*/
public void setSuppressModeC(final boolean suppressModeC) {
this.suppressModeC = suppressModeC;
}
 
/**
* Returns whether or not this symbol is prevented from using subset mode C for numeric data
* compression.
*
* @return whether or not this symbol is prevented from using subset mode C for numeric data
* compression
*/
public boolean getSuppressModeC() {
return this.suppressModeC;
}
 
protected void setCca() {
this.compositeMode = Composite.CCA;
}
 
protected void setCcb() {
this.compositeMode = Composite.CCB;
}
 
protected void setCcc() {
this.compositeMode = Composite.CCC;
}
 
public void unsetCc() {
this.compositeMode = Composite.OFF;
}
 
@Override
protected boolean gs1Supported() {
return true;
}
 
@Override
protected void encode() {
int i, j, k;
final int input_point = 0;
Mode mode, last_mode;
Mode last_set, current_set;
double glyph_count;
int bar_characters = 0, total_sum = 0;
FMode f_state = FMode.LATCHN;
final Mode[] mode_type = new Mode[200];
final int[] mode_length = new int[200];
final int[] values = new int[200];
int c;
int linkage_flag = 0;
int index_point = 0;
int read = 0;
 
this.inputData = toBytes(this.content, ISO_8859_1);
if (this.inputData == null) {
throw new OkapiException("Invalid characters in input data");
}
 
final int sourcelen = this.inputData.length;
 
final FMode[] fset = new FMode[200];
final Mode[] set = new Mode[200]; /* set[] = Calculated mode for each character */
 
if (sourcelen > 170) {
throw new OkapiException("Input data too long");
}
 
/* Detect extended ASCII characters */
for (i = 0; i < sourcelen; i++) {
final int ch = this.inputData[i];
if (ch >= 128 && ch != FNC1 && ch != FNC2 && ch != FNC3 && ch != FNC4) {
fset[i] = FMode.SHIFTF;
} else {
fset[i] = FMode.LATCHN;
}
}
 
/* Decide when to latch to extended mode - Annex E note 3 */
j = 0;
for (i = 0; i < sourcelen; i++) {
if (fset[i] == FMode.SHIFTF) {
j++;
} else {
j = 0;
}
if (j >= 5) {
for (k = i; k > i - 5; k--) {
fset[k] = FMode.LATCHF;
}
}
if (j >= 3 && i == sourcelen - 1) {
for (k = i; k > i - 3; k--) {
fset[k] = FMode.LATCHF;
}
}
}
 
/*
* Decide if it is worth reverting to 646 encodation for a few characters as described in
* 4.3.4.2 (d)
*/
for (i = 1; i < sourcelen; i++) {
if (fset[i - 1] == FMode.LATCHF && fset[i] == FMode.LATCHN) {
/* Detected a change from 8859-1 to 646 - count how long for */
for (j = 0; fset[i + j] == FMode.LATCHN && i + j < sourcelen; j++) {
;
}
if (j < 5 || j < 3 && i + j == sourcelen - 1) {
/* Uses the same figures recommended by Annex E note 3 */
/* Change to shifting back rather than latching back */
for (k = 0; k < j; k++) {
fset[i + k] = FMode.SHIFTN;
}
}
}
}
 
/* Decide on mode using same system as PDF417 and rules of ISO 15417 Annex E */
int letter = this.inputData[input_point];
int numbers = letter >= '0' && letter <= '9' ? 1 : 0;
mode = findSubset(letter, numbers);
mode_type[0] = mode;
mode_length[0] += length(letter, mode);
for (i = 1; i < sourcelen; i++) {
letter = this.inputData[i];
last_mode = mode;
mode = findSubset(letter, numbers);
if (mode == last_mode) {
mode_length[index_point] += length(letter, mode);
} else {
index_point++;
mode_type[index_point] = mode;
mode_length[index_point] = length(letter, mode);
}
if (letter >= '0' && letter <= '9') {
numbers++;
} else {
numbers = 0;
}
}
index_point++;
index_point = reduceSubsetChanges(mode_type, mode_length, index_point);
 
/* Put set data into set[] */
read = 0;
for (i = 0; i < index_point; i++) {
for (j = 0; j < mode_length[i]; j++) {
set[read] = mode_type[i];
read++;
}
}
 
/* Resolve odd length LATCHC blocks */
int cs = 0, nums = 0, fncs = 0;
for (i = 0; i < read; i++) {
if (set[i] == Mode.LATCHC) {
cs++;
if (this.inputData[i] >= '0' && this.inputData[i] <= '9') {
nums++;
} else if (this.inputData[i] == FNC1) {
fncs++;
}
} else {
resolveOddCs(set, i, cs, nums, fncs);
cs = 0;
nums = 0;
fncs = 0;
}
}
resolveOddCs(set, i, cs, nums, fncs);
 
/* Adjust for strings which start with shift characters - make them latch instead */
if (set[0] == Mode.SHIFTA) {
i = 0;
do {
set[i] = Mode.LATCHA;
i++;
} while (set[i] == Mode.SHIFTA);
}
if (set[0] == Mode.SHIFTB) {
i = 0;
do {
set[i] = Mode.LATCHB;
i++;
} while (set[i] == Mode.SHIFTB);
}
 
/*
* Now we can calculate how long the barcode is going to be - and stop it from being too
* long
*/
last_set = Mode.NULL;
glyph_count = 0.0;
for (i = 0; i < sourcelen; i++) {
if (set[i] == Mode.SHIFTA || set[i] == Mode.SHIFTB) {
glyph_count += 1.0;
}
if (fset[i] == FMode.SHIFTF || fset[i] == FMode.SHIFTN) {
glyph_count += 1.0;
}
if (set[i] == Mode.LATCHA || set[i] == Mode.LATCHB || set[i] == Mode.LATCHC) {
if (set[i] != last_set) {
last_set = set[i];
glyph_count += 1.0;
}
}
if (i == 0) {
if (fset[i] == FMode.LATCHF) {
glyph_count += 2.0;
}
} else {
if (fset[i] == FMode.LATCHF && fset[i - 1] != FMode.LATCHF) {
glyph_count += 2.0;
}
if (fset[i] != FMode.LATCHF && fset[i - 1] == FMode.LATCHF) {
glyph_count += 2.0;
}
}
if (set[i] == Mode.LATCHC) {
if (this.inputData[i] == FNC1) {
glyph_count += 1.0;
} else {
glyph_count += 0.5;
}
} else {
glyph_count += 1.0;
}
}
if (glyph_count > 80.0) {
throw new OkapiException("Input data too long");
}
 
info("Encoding: ");
 
/* So now we know what start character to use - we can get on with it! */
if (this.readerInit) {
/* Reader Initialisation mode */
switch (set[0]) {
case LATCHA:
values[0] = 103;
current_set = Mode.LATCHA;
values[1] = 96;
bar_characters++;
info("STARTA FNC3 ");
break;
case LATCHB:
values[0] = 104;
current_set = Mode.LATCHB;
values[1] = 96;
bar_characters++;
info("STARTB FNC3 ");
break;
default: /* Start C */
values[0] = 104;
values[1] = 96;
values[2] = 99;
bar_characters += 2;
current_set = Mode.LATCHC;
info("STARTB FNC3 CODEC ");
break;
}
} else {
/* Normal mode */
switch (set[0]) {
case LATCHA:
values[0] = 103;
current_set = Mode.LATCHA;
info("STARTA ");
break;
case LATCHB:
values[0] = 104;
current_set = Mode.LATCHB;
info("STARTB ");
break;
default:
values[0] = 105;
current_set = Mode.LATCHC;
info("STARTC ");
break;
}
}
bar_characters++;
 
if (this.inputDataType == DataType.GS1) {
values[1] = 102;
bar_characters++;
info("FNC1 ");
}
 
if (fset[0] == FMode.LATCHF) {
switch (current_set) {
case LATCHA:
values[bar_characters] = 101;
values[bar_characters + 1] = 101;
info("FNC4 FNC4 ");
break;
case LATCHB:
values[bar_characters] = 100;
values[bar_characters + 1] = 100;
info("FNC4 FNC4 ");
break;
}
bar_characters += 2;
f_state = FMode.LATCHF;
}
 
/* Encode the data */
read = 0;
do {
 
if (read != 0 && set[read] != current_set) { /* Latch different code set */
switch (set[read]) {
case LATCHA:
values[bar_characters] = 101;
bar_characters++;
current_set = Mode.LATCHA;
info("CODEA ");
break;
case LATCHB:
values[bar_characters] = 100;
bar_characters++;
current_set = Mode.LATCHB;
info("CODEB ");
break;
case LATCHC:
values[bar_characters] = 99;
bar_characters++;
current_set = Mode.LATCHC;
info("CODEC ");
break;
}
}
 
if (read != 0) {
if (fset[read] == FMode.LATCHF && f_state == FMode.LATCHN) {
/* Latch beginning of extended mode */
switch (current_set) {
case LATCHA:
values[bar_characters] = 101;
values[bar_characters + 1] = 101;
info("FNC4 FNC4 ");
break;
case LATCHB:
values[bar_characters] = 100;
values[bar_characters + 1] = 100;
info("FNC4 FNC4 ");
break;
}
bar_characters += 2;
f_state = FMode.LATCHF;
}
if (fset[read] == FMode.LATCHN && f_state == FMode.LATCHF) {
/* Latch end of extended mode */
switch (current_set) {
case LATCHA:
values[bar_characters] = 101;
values[bar_characters + 1] = 101;
info("FNC4 FNC4 ");
break;
case LATCHB:
values[bar_characters] = 100;
values[bar_characters + 1] = 100;
info("FNC4 FNC4 ");
break;
}
bar_characters += 2;
f_state = FMode.LATCHN;
}
}
 
if (fset[read] == FMode.SHIFTF || fset[read] == FMode.SHIFTN) {
/* Shift to or from extended mode */
switch (current_set) {
case LATCHA:
values[bar_characters] = 101;
info("FNC4 ");
break;
case LATCHB:
values[bar_characters] = 100;
info("FNC4 ");
break;
}
bar_characters++;
}
 
if (set[read] == Mode.SHIFTA || set[read] == Mode.SHIFTB) {
/* Insert shift character */
values[bar_characters] = 98;
info("SHFT ");
bar_characters++;
}
 
/* Encode data characters */
c = this.inputData[read];
switch (set[read]) {
case SHIFTA:
case LATCHA:
if (c == FNC1) {
values[bar_characters] = 102;
info("FNC1 ");
} else if (c == FNC2) {
values[bar_characters] = 97;
info("FNC2 ");
} else if (c == FNC3) {
values[bar_characters] = 96;
info("FNC3 ");
} else if (c == FNC4) {
values[bar_characters] = 101;
info("FNC4 ");
} else if (c > 127) {
if (c < 160) {
values[bar_characters] = c - 128 + 64;
} else {
values[bar_characters] = c - 128 - 32;
}
infoSpace(values[bar_characters]);
} else {
if (c < 32) {
values[bar_characters] = c + 64;
} else {
values[bar_characters] = c - 32;
}
infoSpace(values[bar_characters]);
}
bar_characters++;
read++;
break;
case SHIFTB:
case LATCHB:
if (c == FNC1) {
values[bar_characters] = 102;
info("FNC1 ");
} else if (c == FNC2) {
values[bar_characters] = 97;
info("FNC2 ");
} else if (c == FNC3) {
values[bar_characters] = 96;
info("FNC3 ");
} else if (c == FNC4) {
values[bar_characters] = 100;
info("FNC4 ");
} else if (c > 127) {
values[bar_characters] = c - 32 - 128;
infoSpace(values[bar_characters]);
} else {
values[bar_characters] = c - 32;
infoSpace(values[bar_characters]);
}
bar_characters++;
read++;
break;
case LATCHC:
if (c == FNC1) {
values[bar_characters] = 102;
info("FNC1 ");
bar_characters++;
read++;
} else {
final int d = this.inputData[read + 1];
final int weight = 10 * (c - '0') + d - '0';
values[bar_characters] = weight;
infoSpace(values[bar_characters]);
bar_characters++;
read += 2;
}
break;
}
 
} while (read < sourcelen);
 
infoLine();
 
/*
* "...note that the linkage flag is an extra code set character between the last data
* character and the Symbol Check Character" (GS1 Specification)
*/
 
/* Linkage flags in GS1-128 are determined by ISO/IEC 24723 section 7.4 */
 
switch (this.compositeMode) {
case CCA:
case CCB:
/* CC-A or CC-B 2D component */
switch (set[sourcelen - 1]) {
case LATCHA:
linkage_flag = 100;
break;
case LATCHB:
linkage_flag = 99;
break;
case LATCHC:
linkage_flag = 101;
break;
}
infoLine("Linkage Flag: " + linkage_flag);
break;
case CCC:
/* CC-C 2D component */
switch (set[sourcelen - 1]) {
case LATCHA:
linkage_flag = 99;
break;
case LATCHB:
linkage_flag = 101;
break;
case LATCHC:
linkage_flag = 100;
break;
}
infoLine("Linkage Flag: " + linkage_flag);
break;
default:
break;
}
 
if (linkage_flag != 0) {
values[bar_characters] = linkage_flag;
bar_characters++;
}
 
infoLine("Data Codewords: " + bar_characters);
 
/* Check digit calculation */
for (i = 0; i < bar_characters; i++) {
total_sum += i == 0 ? values[i] : values[i] * i;
}
final int checkDigit = total_sum % 103;
infoLine("Check Digit: " + checkDigit);
 
/* Build pattern string */
final StringBuilder dest = new StringBuilder(6 * bar_characters + 6 + 7);
for (i = 0; i < bar_characters; i++) {
dest.append(CODE128_TABLE[values[i]]);
}
dest.append(CODE128_TABLE[checkDigit]);
dest.append(CODE128_TABLE[106]); // stop character
 
/* Readable text */
if (this.inputDataType != DataType.GS1) {
this.readable = removeFncEscapeSequences(this.content);
if (this.inputDataType == DataType.HIBC) {
this.readable = "*" + this.readable + "*";
}
}
 
if (this.compositeMode == Composite.OFF) {
this.pattern = new String[] { dest.toString() };
this.row_height = new int[] { -1 };
this.row_count = 1;
} else {
/* Add the separator pattern for composite symbols */
this.pattern = new String[] { "0" + dest, dest.toString() };
this.row_height = new int[] { 1, -1 };
this.row_count = 2;
}
}
 
private static String removeFncEscapeSequences(final String s) {
return s.replace(FNC1_STRING, "").replace(FNC2_STRING, "").replace(FNC3_STRING, "").replace(FNC4_STRING, "");
}
 
private void resolveOddCs(final Mode[] set, final int i, final int cs, final int nums, final int fncs) {
if ((nums & 1) != 0) {
int index;
Mode m;
if (i - cs == 0 || fncs > 0) {
// Rule 2: first block -> swap last digit to A or B
index = i - 1;
if (index + 1 < set.length && set[index + 1] != null && set[index + 1] != Mode.LATCHC) {
// next block is either A or B -- match it
m = set[index + 1];
} else {
// next block is C, or there is no next block -- just latch to B
m = Mode.LATCHB;
}
} else {
// Rule 3b: subsequent block -> swap first digit to A or B
// Note that we make an exception for C blocks which contain one (or more) FNC1
// characters,
// since swapping the first digit would place the FNC1 in an invalid position in the
// block
index = i - nums;
if (index - 1 >= 0 && set[index - 1] != null && set[index - 1] != Mode.LATCHC) {
// previous block is either A or B -- match it
m = set[index - 1];
} else {
// previous block is C, or there is no previous block -- just latch to B
m = Mode.LATCHB;
}
}
set[index] = m;
}
}
 
private Mode findSubset(final int letter, final int numbers) {
 
Mode mode;
 
if (letter == FNC1) {
if (numbers % 2 == 0) {
/* ISO 15417 Annex E Note 2 */
/*
* FNC1 may use subset C, so long as it doesn't break data into an odd number of
* digits
*/
mode = Mode.ABORC;
} else {
mode = Mode.AORB;
}
} else if (letter == FNC2 || letter == FNC3 || letter == FNC4) {
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;
}
 
if (this.suppressModeC && mode == Mode.ABORC) {
mode = Mode.AORB;
}
 
return mode;
}
 
private int length(final int letter, final Mode mode) {
if (letter == FNC1 && mode == Mode.ABORC) {
/* ISO 15417 Annex E Note 2 */
/* Logical length used for making subset switching decisions, not actual length */
return 2;
} else {
return 1;
}
}
 
/** Implements rules from ISO 15417 Annex E. Returns the updated index point. */
private int reduceSubsetChanges(final Mode[] mode_type, final int[] mode_length, final int index_point) {
 
int totalLength = 0;
int i, length;
Mode current, last, next;
 
for (i = 0; i < index_point; i++) {
current = mode_type[i];
length = mode_length[i];
if (i != 0) {
last = mode_type[i - 1];
} else {
last = Mode.NULL;
}
if (i != index_point - 1) {
next = mode_type[i + 1];
} else {
next = Mode.NULL;
}
 
/* ISO 15417 Annex E Note 2 */
/* Calculate difference between logical length and actual length in this block */
int extraLength = 0;
for (int j = 0; j < length - extraLength; j++) {
if (length(this.inputData[totalLength + j], current) == 2) {
extraLength++;
}
}
 
if (i == 0) { /* first block */
if (index_point == 1 && length == 2 && current == Mode.ABORC) { /* Rule 1a */
mode_type[i] = Mode.LATCHC;
current = Mode.LATCHC;
}
if (current == Mode.ABORC) {
if (length >= 4) { /* Rule 1b */
mode_type[i] = Mode.LATCHC;
current = Mode.LATCHC;
} else {
mode_type[i] = Mode.AORB;
current = Mode.AORB;
}
}
if (current == Mode.SHIFTA) { /* Rule 1c */
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.AORB && next == Mode.SHIFTA) { /* Rule 1c */
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.AORB) { /* Rule 1d */
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
} else {
if (current == Mode.ABORC && length >= 4) { /* Rule 3 */
mode_type[i] = Mode.LATCHC;
current = Mode.LATCHC;
}
if (current == Mode.ABORC) {
mode_type[i] = Mode.AORB;
current = Mode.AORB;
}
if (current == Mode.AORB && last == Mode.LATCHA) {
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.AORB && last == Mode.LATCHB) {
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.AORB && next == Mode.SHIFTA) {
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.AORB && next == Mode.SHIFTB) {
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.AORB) {
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.SHIFTA && length > 1) { /* Rule 4 */
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.SHIFTB && length > 1) { /* Rule 5 */
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.SHIFTA && last == Mode.LATCHA) {
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.SHIFTB && last == Mode.LATCHB) {
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.SHIFTA && next == Mode.AORB) {
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.SHIFTB && next == Mode.AORB) {
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
if (current == Mode.SHIFTA && last == Mode.LATCHC) {
mode_type[i] = Mode.LATCHA;
current = Mode.LATCHA;
}
if (current == Mode.SHIFTB && last == Mode.LATCHC) {
mode_type[i] = Mode.LATCHB;
current = Mode.LATCHB;
}
} /* Rule 2 is implemented elsewhere, Rule 6 is implied */
 
/* ISO 15417 Annex E Note 2 */
/*
* Convert logical length back to actual length for this block, now that we've decided
* on a subset
*/
mode_length[i] -= extraLength;
totalLength += mode_length[i];
}
 
return combineSubsetBlocks(mode_type, mode_length, index_point);
}
 
/**
* Modifies the specified mode and length arrays to combine adjacent modes of the same type,
* returning the updated index point.
*/
private int combineSubsetBlocks(final Mode[] mode_type, final int[] mode_length, int index_point) {
/* bring together same type blocks */
if (index_point > 1) {
for (int i = 1; i < index_point; i++) {
if (mode_type[i - 1] == mode_type[i]) {
/* bring together */
mode_length[i - 1] = mode_length[i - 1] + mode_length[i];
/* decrease the list */
for (int j = i + 1; j < index_point; j++) {
mode_length[j - 1] = mode_length[j];
mode_type[j - 1] = mode_type[j];
}
index_point--;
i--;
}
}
}
return index_point;
}
 
/** {@inheritDoc} */
@Override
protected int[] getCodewords() {
return getPatternAsCodewords(6);
}
}
/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/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/UspsPackage.java
New file
0,0 → 1,129
/*
* Copyright 2015 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import java.awt.geom.Rectangle2D;
 
/**
* <p>
* Implements USPS Intelligent Mail Package Barcode (IMpb), a linear barcode based on GS1-128.
* Includes additional data checks.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
* @see <a href=
* "https://ribbs.usps.gov/intelligentmail_package/documents/tech_guides/BarcodePackageIMSpec.pdf">IMpb
* Specification</a>
*/
public class UspsPackage extends Symbol {
 
@Override
protected void encode() {
 
if (!this.content.matches("[0-9\\[\\]]+")) {
/* Input must be numeric only */
throw new OkapiException("Invalid IMpb data");
}
 
if (this.content.length() % 2 != 0) {
/* Input must be even length */
throw new OkapiException("Invalid IMpb data");
}
 
final Code128 code128 = new Code128();
code128.unsetCc();
code128.setDataType(DataType.GS1);
code128.setContent(this.content);
 
final boolean fourTwenty = this.content.length() > 4 && this.content.charAt(1) == '4' && this.content.charAt(2) == '2' && this.content.charAt(3) == '0';
 
String hrt = "";
int bracketCount = 0;
for (int i = 0; i < this.content.length(); i++) {
if (this.content.charAt(i) == '[') {
bracketCount++;
}
if (!(fourTwenty && bracketCount < 2)) {
if (this.content.charAt(i) >= '0' && this.content.charAt(i) <= '9') {
hrt += this.content.charAt(i);
}
}
}
 
String spacedHrt = "";
for (int i = 0; i < hrt.length(); i++) {
spacedHrt += hrt.charAt(i);
if (i % 4 == 3) {
spacedHrt += " ";
}
}
 
this.encodeInfo = code128.encodeInfo;
this.readable = spacedHrt;
this.pattern = new String[1];
this.pattern[0] = code128.pattern[0];
this.row_count = 1;
this.row_height = new int[1];
this.row_height[0] = -1;
}
 
@Override
protected void plotSymbol() {
int xBlock;
int x, y, w, h;
boolean black;
final int offset = 20;
final int yoffset = 15;
final String banner = "USPS TRACKING #";
 
this.rectangles.clear();
this.texts.clear();
y = yoffset;
h = 0;
black = true;
x = 0;
for (xBlock = 0; xBlock < this.pattern[0].length(); xBlock++) {
w = this.pattern[0].charAt(xBlock) - '0';
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 = x + w + 2 * offset;
}
black = !black;
x += w;
}
this.symbol_height = h + 2 * yoffset;
 
// Add boundary bars
final Rectangle2D.Double topBar = new Rectangle2D.Double(0, 0, this.symbol_width, 2);
final Rectangle2D.Double bottomBar = new Rectangle2D.Double(0, this.symbol_height - 2, this.symbol_width, 2);
this.rectangles.add(topBar);
this.rectangles.add(bottomBar);
 
this.texts.add(new TextBox(0, this.symbol_height - 6.0, this.symbol_width, this.readable, this.humanReadableAlignment));
this.texts.add(new TextBox(0, 12.0, this.symbol_width, banner, this.humanReadableAlignment));
}
 
/** {@inheritDoc} */
@Override
protected int[] getCodewords() {
return getPatternAsCodewords(6);
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/DataBarExpanded.java
New file
0,0 → 1,1271
/*
* Copyright 2014 Robin Stuart
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package uk.org.okapibarcode.backend;
 
import static uk.org.okapibarcode.backend.DataBarLimited.getWidths;
import static uk.org.okapibarcode.util.Strings.binaryAppend;
 
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
 
/**
* <p>
* Implements GS1 DataBar Expanded Omnidirectional and GS1 DataBar Expanded Stacked Omnidirectional
* according to ISO/IEC 24724:2011.
*
* <p>
* DataBar expanded encodes GS1 data in either a linear or stacked format.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class DataBarExpanded extends Symbol {
 
private static final int[] G_SUM_EXP = { 0, 348, 1388, 2948, 3988 };
 
private static final int[] T_EVEN_EXP = { 4, 20, 52, 104, 204 };
 
private static final int[] MODULES_ODD_EXP = { 12, 10, 8, 6, 4 };
 
private static final int[] MODULES_EVEN_EXP = { 5, 7, 9, 11, 13 };
 
private static final int[] WIDEST_ODD_EXP = { 7, 5, 4, 3, 1 };
 
private static final int[] WIDEST_EVEN_EXP = { 2, 4, 5, 6, 8 };
 
/** Table 14 */
private static final int[] CHECKSUM_WEIGHT_EXP = { 1, 3, 9, 27, 81, 32, 96, 77, 20, 60, 180, 118, 143, 7, 21, 63, 189, 145, 13, 39, 117, 140, 209, 205, 193, 157, 49, 147, 19, 57, 171, 91, 62, 186,
136, 197, 169, 85, 44, 132, 185, 133, 188, 142, 4, 12, 36, 108, 113, 128, 173, 97, 80, 29, 87, 50, 150, 28, 84, 41, 123, 158, 52, 156, 46, 138, 203, 187, 139, 206, 196, 166, 76, 17, 51,
153, 37, 111, 122, 155, 43, 129, 176, 106, 107, 110, 119, 146, 16, 48, 144, 10, 30, 90, 59, 177, 109, 116, 137, 200, 178, 112, 125, 164, 70, 210, 208, 202, 184, 130, 179, 115, 134, 191,
151, 31, 93, 68, 204, 190, 148, 22, 66, 198, 172, 94, 71, 2, 6, 18, 54, 162, 64, 192, 154, 40, 120, 149, 25, 75, 14, 42, 126, 167, 79, 26, 78, 23, 69, 207, 199, 175, 103, 98, 83, 38, 114,
131, 182, 124, 161, 61, 183, 127, 170, 88, 53, 159, 55, 165, 73, 8, 24, 72, 5, 15, 45, 135, 194, 160, 58, 174, 100, 89 };
 
/** Table 15 */
private static final int[] FINDER_PATTERN_EXP = { 1, 8, 4, 1, 1, 1, 1, 4, 8, 1, 3, 6, 4, 1, 1, 1, 1, 4, 6, 3, 3, 4, 6, 1, 1, 1, 1, 6, 4, 3, 3, 2, 8, 1, 1, 1, 1, 8, 2, 3, 2, 6, 5, 1, 1, 1, 1, 5, 6,
2, 2, 2, 9, 1, 1, 1, 1, 9, 2, 2 };
 
/** Table 16 */
private static final int[] FINDER_SEQUENCE = { 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 3, 8, 0, 0, 0, 0, 0, 0, 0, 1, 10, 3, 8, 5, 0, 0, 0, 0, 0, 0, 1, 10, 3, 8, 7,
12, 0, 0, 0, 0, 0, 1, 10, 3, 8, 9, 12, 11, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 10, 9, 0, 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 0, 1, 2, 3, 4, 5, 8, 7, 10, 9,
12, 11 };
 
private static final int[] WEIGHT_ROWS = { 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 3, 4, 13, 14, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, 14, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 3, 4, 13, 14, 11, 12, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18,
3, 4, 13, 14, 15, 16, 21, 22, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 17, 18, 15, 16,
0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 17, 18, 19, 20, 21, 22, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 13, 14, 11, 12, 17, 18, 15, 16, 21, 22, 19, 20 };
 
private enum EncodeMode {
NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO
}
 
private boolean linkageFlag;
private int preferredColumns = 2;
private boolean stacked = true;
 
public DataBarExpanded() {
this.inputDataType = DataType.GS1;
}
 
@Override
public void setDataType(final DataType dataType) {
if (dataType != Symbol.DataType.GS1) {
throw new IllegalArgumentException("Only GS1 data type is supported for DataBar Expanded symbology.");
}
}
 
@Override
protected boolean gs1Supported() {
return true;
}
 
/**
* Sets the preferred width of a stacked symbol by selecting the number of "columns" or symbol
* segments in each row of data.
*
* @param columns the number of segments in each row
*/
public void setPreferredColumns(final int columns) {
if (columns < 1 || columns > 10) {
throw new IllegalArgumentException("Invalid column count: " + columns);
}
this.preferredColumns = columns;
}
 
/**
* Returns the preferred width of a stacked symbol by selecting the number of "columns" or
* symbol segments in each row of data.
*
* @return the number of segments in each row
*/
public int getPreferredColumns() {
return this.preferredColumns;
}
 
/**
* Sets whether or not this symbology is stacked.
*
* @param stacked <tt>true</tt> for GS1 DataBar Expanded Stacked Omnidirectional, <tt>false</tt>
* for GS1 DataBar Expanded Omnidirectional
*/
public void setStacked(final boolean stacked) {
this.stacked = stacked;
}
 
/**
* Returns whether or not this symbology is stacked.
*
* @return <tt>true</tt> for GS1 DataBar Expanded Stacked Omnidirectional, <tt>false</tt> for
* GS1 DataBar Expanded Omnidirectional
*/
public boolean isStacked() {
return this.stacked;
}
 
protected void setLinkageFlag(final boolean linkageFlag) {
this.linkageFlag = linkageFlag;
}
 
@Override
protected void encode() {
int i;
int j;
int k;
int data_chars;
final int[] vs = new int[21];
final int[] group = new int[21];
final int[] v_odd = new int[21];
final int[] v_even = new int[21];
final int[][] char_widths = new int[21][8];
int checksum;
int row;
int check_char;
int c_group;
int c_odd;
int c_even;
final int[] check_widths = new int[8];
int pattern_width;
final int[] elements = new int[235];
int codeblocks;
int stack_rows;
int blocksPerRow;
int current_block;
int current_row;
boolean special_case_row;
int elements_in_sub;
int reader;
int writer;
final int[] sub_elements = new int[235];
int l;
int symbol_row;
String separator_pattern;
boolean black;
boolean left_to_right;
int compositeOffset;
 
this.inputData = toBytes(this.content, StandardCharsets.US_ASCII);
 
final StringBuilder binaryString = new StringBuilder(this.inputData.length * 8);
 
if (this.linkageFlag) {
binaryString.append('1');
compositeOffset = 1;
} else {
binaryString.append('0');
compositeOffset = 0;
}
 
final int encodingMethod = calculateBinaryString(this.inputData, binaryString); // updates
// binaryString
infoLine("Encoding Method: " + encodingMethod);
logBinaryStringInfo(binaryString);
 
data_chars = binaryString.length() / 12;
 
info("Data Characters: ");
for (i = 0; i < data_chars; i++) {
vs[i] = 0;
for (j = 0; j < 12; j++) {
if (binaryString.charAt(i * 12 + j) == '1') {
vs[i] += 2048 >> j;
}
}
infoSpace(vs[i]);
}
infoLine();
 
for (i = 0; i < data_chars; i++) {
if (vs[i] <= 347) {
group[i] = 1;
}
if (vs[i] >= 348 && vs[i] <= 1387) {
group[i] = 2;
}
if (vs[i] >= 1388 && vs[i] <= 2947) {
group[i] = 3;
}
if (vs[i] >= 2948 && vs[i] <= 3987) {
group[i] = 4;
}
if (vs[i] >= 3988) {
group[i] = 5;
}
v_odd[i] = (vs[i] - G_SUM_EXP[group[i] - 1]) / T_EVEN_EXP[group[i] - 1];
v_even[i] = (vs[i] - G_SUM_EXP[group[i] - 1]) % T_EVEN_EXP[group[i] - 1];
 
int[] widths = getWidths(v_odd[i], MODULES_ODD_EXP[group[i] - 1], 4, WIDEST_ODD_EXP[group[i] - 1], 0);
char_widths[i][0] = widths[0];
char_widths[i][2] = widths[1];
char_widths[i][4] = widths[2];
char_widths[i][6] = widths[3];
 
widths = getWidths(v_even[i], MODULES_EVEN_EXP[group[i] - 1], 4, WIDEST_EVEN_EXP[group[i] - 1], 1);
char_widths[i][1] = widths[0];
char_widths[i][3] = widths[1];
char_widths[i][5] = widths[2];
char_widths[i][7] = widths[3];
}
 
/* 7.2.6 Check character */
/*
* The checksum value is equal to the mod 211 residue of the weighted sum of the widths of
* the elements in the data characters.
*/
checksum = 0;
for (i = 0; i < data_chars; i++) {
row = WEIGHT_ROWS[(data_chars - 2) / 2 * 21 + i];
for (j = 0; j < 8; j++) {
checksum += char_widths[i][j] * CHECKSUM_WEIGHT_EXP[row * 8 + j];
 
}
}
 
check_char = 211 * (data_chars + 1 - 4) + checksum % 211;
 
infoLine("Check Character: " + check_char);
 
c_group = 1;
if (check_char >= 348 && check_char <= 1387) {
c_group = 2;
}
if (check_char >= 1388 && check_char <= 2947) {
c_group = 3;
}
if (check_char >= 2948 && check_char <= 3987) {
c_group = 4;
}
if (check_char >= 3988) {
c_group = 5;
}
 
c_odd = (check_char - G_SUM_EXP[c_group - 1]) / T_EVEN_EXP[c_group - 1];
c_even = (check_char - G_SUM_EXP[c_group - 1]) % T_EVEN_EXP[c_group - 1];
 
int[] widths = getWidths(c_odd, MODULES_ODD_EXP[c_group - 1], 4, WIDEST_ODD_EXP[c_group - 1], 0);
check_widths[0] = widths[0];
check_widths[2] = widths[1];
check_widths[4] = widths[2];
check_widths[6] = widths[3];
 
widths = getWidths(c_even, MODULES_EVEN_EXP[c_group - 1], 4, WIDEST_EVEN_EXP[c_group - 1], 1);
check_widths[1] = widths[0];
check_widths[3] = widths[1];
check_widths[5] = widths[2];
check_widths[7] = widths[3];
 
/* Initialise element array */
pattern_width = ((data_chars + 1) / 2 + (data_chars + 1 & 1)) * 5 + (data_chars + 1) * 8 + 4;
for (i = 0; i < pattern_width; i++) {
elements[i] = 0;
}
 
elements[0] = 1;
elements[1] = 1;
elements[pattern_width - 2] = 1;
elements[pattern_width - 1] = 1;
 
/* Put finder patterns in element array */
for (i = 0; i < (data_chars + 1) / 2 + (data_chars + 1 & 1); i++) {
k = ((data_chars + 1 - 2) / 2 + (data_chars + 1 & 1) - 1) * 11 + i;
for (j = 0; j < 5; j++) {
elements[21 * i + j + 10] = FINDER_PATTERN_EXP[(FINDER_SEQUENCE[k] - 1) * 5 + j];
}
}
 
/* Put check character in element array */
for (i = 0; i < 8; i++) {
elements[i + 2] = check_widths[i];
}
 
/* Put forward reading data characters in element array */
for (i = 1; i < data_chars; i += 2) {
for (j = 0; j < 8; j++) {
elements[(i - 1) / 2 * 21 + 23 + j] = char_widths[i][j];
}
}
 
/* Put reversed data characters in element array */
for (i = 0; i < data_chars; i += 2) {
for (j = 0; j < 8; j++) {
elements[i / 2 * 21 + 15 + j] = char_widths[i][7 - j];
}
}
 
if (!this.stacked) {
/* Copy elements into symbol */
this.row_count = 1 + compositeOffset;
this.row_height = new int[1 + compositeOffset];
this.row_height[0 + compositeOffset] = -1;
this.pattern = new String[1 + compositeOffset];
 
writer = 0;
black = false;
final StringBuilder pat = new StringBuilder("0");
final StringBuilder separator_binary = new StringBuilder();
for (i = 0; i < pattern_width; i++) {
pat.append((char) (elements[i] + '0'));
for (j = 0; j < elements[i]; j++) {
if (black) {
separator_binary.append('0');
} else {
separator_binary.append('1');
}
}
black = !black;
writer += elements[i];
}
this.pattern[0 + compositeOffset] = pat.toString();
 
separator_binary.setCharAt(0, '0');
separator_binary.setCharAt(1, '0');
separator_binary.setCharAt(2, '0');
separator_binary.setCharAt(3, '0');
separator_binary.delete(writer - 4, separator_binary.length());
for (j = 0; j < writer / 49; j++) {
k = 49 * j + 18;
for (i = 0; i < 15; i++) {
if (separator_binary.charAt(i + k - 1) == '1' && separator_binary.charAt(i + k) == '1') {
separator_binary.setCharAt(i + k, '0');
}
}
}
if (this.linkageFlag) {
// Add composite code separator
this.pattern[0] = bin2pat(separator_binary);
this.row_height[0] = 1;
}
 
} else {
/* RSS Expanded Stacked */
codeblocks = (data_chars + 1) / 2 + (data_chars + 1) % 2;
 
blocksPerRow = this.preferredColumns;
 
if (this.linkageFlag && blocksPerRow == 1) {
/*
* "There shall be a minimum of four symbol characters in the first row of an RSS
* Expanded Stacked symbol when it is the linear component of an EAN.UCC Composite
* symbol."
*/
blocksPerRow = 2;
}
 
stack_rows = codeblocks / blocksPerRow;
if (codeblocks % blocksPerRow > 0) {
stack_rows++;
}
 
this.row_count = stack_rows * 4 - 3;
this.row_height = new int[this.row_count + compositeOffset];
this.pattern = new String[this.row_count + compositeOffset];
symbol_row = 0;
 
current_block = 0;
for (current_row = 1; current_row <= stack_rows; current_row++) {
for (i = 0; i < 235; i++) {
sub_elements[i] = 0;
}
special_case_row = false;
 
/* Row Start */
sub_elements[0] = 1;
sub_elements[1] = 1;
elements_in_sub = 2;
 
/* Row Data */
reader = 0;
do {
if ((blocksPerRow & 1) != 0 || (current_row & 1) != 0
|| current_row == stack_rows && codeblocks != current_row * blocksPerRow && (current_row * blocksPerRow - codeblocks & 1) != 0) {
/* left to right */
left_to_right = true;
i = 2 + current_block * 21;
for (j = 0; j < 21; j++) {
if (i + j < pattern_width) {
sub_elements[j + reader * 21 + 2] = elements[i + j];
elements_in_sub++;
}
}
} else {
/* right to left */
left_to_right = false;
if (current_row * blocksPerRow < codeblocks) {
/* a full row */
i = 2 + (current_row * blocksPerRow - reader - 1) * 21;
for (j = 0; j < 21; j++) {
if (i + j < pattern_width) {
sub_elements[20 - j + reader * 21 + 2] = elements[i + j];
elements_in_sub++;
}
}
} else {
/* a partial row */
k = current_row * blocksPerRow - codeblocks;
l = current_row * blocksPerRow - reader - 1;
i = 2 + (l - k) * 21;
for (j = 0; j < 21; j++) {
if (i + j < pattern_width) {
sub_elements[20 - j + reader * 21 + 2] = elements[i + j];
elements_in_sub++;
}
}
}
}
reader++;
current_block++;
} while (reader < blocksPerRow && current_block < codeblocks);
 
/* Row Stop */
sub_elements[elements_in_sub] = 1;
sub_elements[elements_in_sub + 1] = 1;
elements_in_sub += 2;
 
black = true;
StringBuilder pat = new StringBuilder();
this.row_height[symbol_row + compositeOffset] = -1;
 
if ((current_row & 1) != 0) {
pat.append('0');
black = false;
} else {
if (current_row == stack_rows && codeblocks != current_row * blocksPerRow && (current_row * blocksPerRow - codeblocks & 1) != 0) {
/* Special case bottom row */
special_case_row = true;
sub_elements[0] = 2;
pat.append('0');
black = false;
}
}
 
writer = 0;
 
final StringBuilder separator_binary = new StringBuilder();
for (i = 0; i < elements_in_sub; i++) {
pat.append((char) (sub_elements[i] + '0'));
for (j = 0; j < sub_elements[i]; j++) {
separator_binary.append(black ? '0' : '1');
}
black = !black;
writer += sub_elements[i];
}
this.pattern[symbol_row + compositeOffset] = pat.toString();
separator_binary.setCharAt(0, '0');
separator_binary.setCharAt(1, '0');
separator_binary.setCharAt(2, '0');
separator_binary.setCharAt(3, '0');
separator_binary.delete(writer - 4, separator_binary.length());
for (j = 0; j < reader; j++) {
k = 49 * j + (special_case_row ? 19 : 18);
if (left_to_right) {
for (i = 0; i < 15; i++) {
if (separator_binary.charAt(i + k - 1) == '1' && separator_binary.charAt(i + k) == '1') {
separator_binary.setCharAt(i + k, '0');
}
}
} else {
for (i = 14; i >= 0; i--) {
if (separator_binary.charAt(i + k + 1) == '1' && separator_binary.charAt(i + k) == '1') {
separator_binary.setCharAt(i + k, '0');
}
}
}
}
separator_pattern = bin2pat(separator_binary);
 
if (current_row == 1 && this.linkageFlag) {
// Add composite code separator
this.row_height[0] = 1;
this.pattern[0] = separator_pattern;
}
 
if (current_row != 1) {
/* middle separator pattern (above current row) */
pat = new StringBuilder("05");
for (j = 5; j < 49 * blocksPerRow; j += 2) {
pat.append("11");
}
this.pattern[symbol_row - 2 + compositeOffset] = pat.toString();
this.row_height[symbol_row - 2 + compositeOffset] = 1;
/* bottom separator pattern (above current row) */
this.row_height[symbol_row - 1 + compositeOffset] = 1;
this.pattern[symbol_row - 1 + compositeOffset] = separator_pattern;
}
 
if (current_row != stack_rows) {
this.row_height[symbol_row + 1 + compositeOffset] = 1;
this.pattern[symbol_row + 1 + compositeOffset] = separator_pattern;
}
 
symbol_row += 4;
}
this.readable = "";
this.row_count += compositeOffset;
}
}
 
/** Handles all data encodation from section 7.2.5 of ISO/IEC 24724. */
private static int calculateBinaryString(final int[] inputData, final StringBuilder binaryString) {
 
EncodeMode last_mode = EncodeMode.NUMERIC;
int i;
boolean latch;
int remainder, d1, d2, value;
String padstring;
int current_length;
 
/*
* Decide whether a compressed data field is required and if so what method to use: method 2
* = no compressed data field
*/
 
int encodingMethod;
if (inputData.length >= 16 && inputData[0] == '0' && inputData[1] == '1') {
/* (01) and other AIs */
encodingMethod = 1;
} else {
/* any AIs */
encodingMethod = 2;
}
 
if (inputData.length >= 20 && encodingMethod == 1 && inputData[2] == '9' && inputData[16] == '3') {
 
/* Possibly encoding method > 2 */
 
if (inputData.length >= 26 && inputData[17] == '1') {
/* Methods 3, 7, 9, 11 and 13 */
if (inputData[18] == '0') {
/* (01) and (310x), weight in kilos */
double weight = 0;
for (i = 0; i < 6; i++) {
weight *= 10;
weight += inputData[20 + i] - '0';
}
if (weight < 99_999) { /* Maximum weight = 99999 */
if (inputData[19] == '3' && inputData.length == 26) {
/* (01) and (3103) */
weight /= 1000.0;
if (weight <= 32.767) {
encodingMethod = 3;
}
}
if (inputData.length == 34) {
if (inputData[26] == '1' && inputData[27] == '1') {
/* (01), (310x) and (11) - metric weight and production date */
encodingMethod = 7;
}
if (inputData[26] == '1' && inputData[27] == '3') {
/* (01), (310x) and (13) - metric weight and packaging date */
encodingMethod = 9;
}
if (inputData[26] == '1' && inputData[27] == '5') {
/* (01), (310x) and (15) - metric weight and "best before" date */
encodingMethod = 11;
}
if (inputData[26] == '1' && inputData[27] == '7') {
/* (01), (310x) and (17) - metric weight and expiration date */
encodingMethod = 13;
}
}
}
}
}
 
if (inputData.length >= 26 && inputData[17] == '2') {
/* Methods 4, 8, 10, 12 and 14 */
if (inputData[18] == '0') {
/* (01) and (320x), weight in pounds */
double weight = 0;
for (i = 0; i < 6; i++) {
weight *= 10;
weight += inputData[20 + i] - '0';
}
if (weight < 99_999) { /* Maximum weight = 99999 */
if ((inputData[19] == '2' || inputData[19] == '3') && inputData.length == 26) {
/* (01) and (3202)/(3203) */
if (inputData[19] == '3') {
weight /= 1000.0;
if (weight <= 22.767) {
encodingMethod = 4;
}
} else {
weight /= 100.0;
if (weight <= 99.99) {
encodingMethod = 4;
}
}
}
if (inputData.length == 34) {
if (inputData[26] == '1' && inputData[27] == '1') {
/* (01), (320x) and (11) - English weight and production date */
encodingMethod = 8;
}
if (inputData[26] == '1' && inputData[27] == '3') {
/* (01), (320x) and (13) - English weight and packaging date */
encodingMethod = 10;
}
if (inputData[26] == '1' && inputData[27] == '5') {
/* (01), (320x) and (15) - English weight and "best before" date */
encodingMethod = 12;
}
if (inputData[26] == '1' && inputData[27] == '7') {
/* (01), (320x) and (17) - English weight and expiration date */
encodingMethod = 14;
}
}
}
}
}
 
if (inputData[17] == '9') {
/* Methods 5 and 6 */
if (inputData[18] == '2' && inputData[19] >= '0' && inputData[19] <= '3') {
/* (01) and (392x) */
encodingMethod = 5;
}
if (inputData[18] == '3' && inputData[19] >= '0' && inputData[19] <= '3') {
/* (01) and (393x) */
encodingMethod = 6;
}
}
}
 
/* Encoding method - Table 10 */
/* Variable length symbol bit field is just given a place holder (XX) for the time being */
int read_posn;
switch (encodingMethod) {
case 1:
binaryString.append("1XX");
read_posn = 16;
break;
case 2:
binaryString.append("00XX");
read_posn = 0;
break;
case 3:
binaryString.append("0100");
read_posn = inputData.length;
break;
case 4:
binaryString.append("0101");
read_posn = inputData.length;
break;
case 5:
binaryString.append("01100XX");
read_posn = 20;
break;
case 6:
binaryString.append("01101XX");
read_posn = 23;
break;
default: /* modes 7 (0111000) to 14 (0111111) */
binaryString.append("0" + Integer.toBinaryString(56 + encodingMethod - 7));
read_posn = inputData.length;
break;
}
 
/*
* Verify that the data to be placed in the compressed data field is all numeric data before
* carrying out compression
*/
for (i = 0; i < read_posn; i++) {
if (inputData[i] < '0' || inputData[i] > '9') {
/* Something is wrong */
throw new OkapiException("Invalid characters in input data");
}
}
 
/* Now encode the compressed data field */
 
if (encodingMethod == 1) {
/* Encoding method field "1" - general item identification data */
binaryAppend(binaryString, inputData[2] - '0', 4);
for (i = 1; i < 5; i++) {
final int group = parseInt(inputData, i * 3, 3);
binaryAppend(binaryString, group, 10);
}
}
 
if (encodingMethod == 3 || encodingMethod == 4) {
/* Encoding method field "0100" - variable weight item (0,001 kilogram increments) */
/*
* Encoding method field "0101" - variable weight item (0,01 or 0,001 pound increment)
*/
for (i = 1; i < 5; i++) {
final int group = parseInt(inputData, i * 3, 3);
binaryAppend(binaryString, group, 10);
}
int group = parseInt(inputData, 20, 6);
if (encodingMethod == 4 && inputData[19] == '3') {
group += 10_000;
}
binaryAppend(binaryString, group, 15);
}
 
if (encodingMethod == 5 || encodingMethod == 6) {
/* Encoding method field "01100" - variable measure item and price */
/*
* Encoding method "01101" - variable measure item and price with ISO 4217 currency code
*/
for (i = 1; i < 5; i++) {
final int group = parseInt(inputData, i * 3, 3);
binaryAppend(binaryString, group, 10);
}
binaryAppend(binaryString, inputData[19] - '0', 2);
if (encodingMethod == 6) {
final int currency = parseInt(inputData, 20, 3);
binaryAppend(binaryString, currency, 10);
}
}
 
if (encodingMethod >= 7 && encodingMethod <= 14) {
/*
* Encoding method fields "0111000" through "0111111" - variable weight item plus date
*/
for (i = 1; i < 5; i++) {
final int group = parseInt(inputData, i * 3, 3);
binaryAppend(binaryString, group, 10);
}
int weight = inputData[19] - '0';
for (i = 0; i < 5; i++) {
weight *= 10;
weight += inputData[21 + i] - '0';
}
binaryAppend(binaryString, weight, 20);
int date;
if (inputData.length == 34) {
/* Date information is included */
date = parseInt(inputData, 28, 2) * 384;
date += (parseInt(inputData, 30, 2) - 1) * 32;
date += parseInt(inputData, 32, 2);
} else {
date = 38_400;
}
binaryAppend(binaryString, date, 16);
}
 
/*
* The compressed data field has been processed if appropriate - the rest of the data (if
* any) goes into a general-purpose data compaction field
*/
 
final int[] generalField = Arrays.copyOfRange(inputData, read_posn, inputData.length);
 
if (generalField.length != 0) {
 
latch = false;
final EncodeMode[] generalFieldType = new EncodeMode[generalField.length];
 
for (i = 0; i < generalField.length; i++) {
/* Tables 11, 12, 13 - ISO/IEC 646 encodation */
final int c = generalField[i];
EncodeMode mode;
if (c == FNC1) {
// FNC1 can be encoded in any system
mode = EncodeMode.ANY_ENC;
} else if (c >= '0' && c <= '9') {
// numbers can be encoded in any system, but will usually narrow down to numeric
// encodation
mode = EncodeMode.ANY_ENC;
} else if (c >= 'A' && c <= 'Z' || c == '*' || c == ',' || c == '-' || c == '.' || c == '/') {
// alphanumeric encodation or ISO/IEC encodation
mode = EncodeMode.ALPHA_OR_ISO;
} else if (c >= 'a' && c <= 'z' || c == '!' || c == '"' || c == '%' || c == '&' || c == '\'' || c == '(' || c == ')' || c == '+' || c == ':' || c == ';' || c == '<' || c == '='
|| c == '>' || c == '?' || c == '_' || c == ' ') {
// ISO/IEC encodation
mode = EncodeMode.ISOIEC;
} else {
// unable to encode this character
mode = EncodeMode.INVALID_CHAR;
latch = true;
}
generalFieldType[i] = mode;
}
 
if (latch) {
throw new OkapiException("Invalid characters in input data");
}
 
for (i = 0; i < generalField.length - 1; i++) {
if (generalFieldType[i] == EncodeMode.ISOIEC && generalField[i + 1] == FNC1) {
generalFieldType[i + 1] = EncodeMode.ISOIEC;
}
}
 
for (i = 0; i < generalField.length - 1; i++) {
if (generalFieldType[i] == EncodeMode.ALPHA_OR_ISO && generalField[i + 1] == FNC1) {
generalFieldType[i + 1] = EncodeMode.ALPHA_OR_ISO;
}
}
 
latch = applyGeneralFieldRules(generalFieldType); // modifies generalFieldType
 
/* Set initial mode if not NUMERIC */
if (generalFieldType[0] == EncodeMode.ALPHA) {
binaryString.append("0000"); /* Alphanumeric latch */
last_mode = EncodeMode.ALPHA;
}
if (generalFieldType[0] == EncodeMode.ISOIEC) {
binaryString.append("0000"); /* Alphanumeric latch */
binaryString.append("00100"); /* ISO/IEC 646 latch */
last_mode = EncodeMode.ISOIEC;
}
 
i = 0;
do {
switch (generalFieldType[i]) {
case NUMERIC:
if (last_mode != EncodeMode.NUMERIC) {
binaryString.append("000"); /* Numeric latch */
}
if (generalField[i] != FNC1) {
d1 = generalField[i] - '0';
} else {
d1 = 10;
}
if (generalField[i + 1] != FNC1) {
d2 = generalField[i + 1] - '0';
} else {
d2 = 10;
}
value = 11 * d1 + d2 + 8;
binaryAppend(binaryString, value, 7);
i += 2;
last_mode = EncodeMode.NUMERIC;
break;
 
case ALPHA:
if (i != 0) {
if (last_mode == EncodeMode.NUMERIC) {
binaryString.append("0000"); /* Alphanumeric latch */
}
if (last_mode == EncodeMode.ISOIEC) {
binaryString.append("00100"); /* Alphanumeric latch */
}
}
if (generalField[i] >= '0' && generalField[i] <= '9') {
value = generalField[i] - 43;
binaryAppend(binaryString, value, 5);
}
if (generalField[i] >= 'A' && generalField[i] <= 'Z') {
value = generalField[i] - 33;
binaryAppend(binaryString, value, 6);
}
last_mode = EncodeMode.ALPHA;
if (generalField[i] == FNC1) {
binaryString.append("01111");
// TODO: FNC1 should act as an implicit numeric latch, so the commented out
// line below should be correct, but ZXing cannot
// read barcodes which use FNC1 as an implicit numeric latch... so for now,
// and in order to achieve widest compatibility,
// we waste 3 bits and don't perform the implicit mode change (see
// https://sourceforge.net/p/zint/tickets/145/)
// last_mode = EncodeMode.NUMERIC;
} /* FNC1 / Numeric latch */
if (generalField[i] == '*') {
binaryString.append("111010"); /* asterisk */
}
if (generalField[i] == ',') {
binaryString.append("111011"); /* comma */
}
if (generalField[i] == '-') {
binaryString.append("111100"); /* minus or hyphen */
}
if (generalField[i] == '.') {
binaryString.append("111101"); /* period or full stop */
}
if (generalField[i] == '/') {
binaryString.append("111110"); /* slash or solidus */
}
i++;
break;
 
case ISOIEC:
if (i != 0) {
if (last_mode == EncodeMode.NUMERIC) {
binaryString.append("0000"); /* Alphanumeric latch */
binaryString.append("00100"); /* ISO/IEC 646 latch */
}
if (last_mode == EncodeMode.ALPHA) {
binaryString.append("00100"); /* ISO/IEC 646 latch */
}
}
if (generalField[i] >= '0' && generalField[i] <= '9') {
value = generalField[i] - 43;
binaryAppend(binaryString, value, 5);
}
if (generalField[i] >= 'A' && generalField[i] <= 'Z') {
value = generalField[i] - 1;
binaryAppend(binaryString, value, 7);
}
if (generalField[i] >= 'a' && generalField[i] <= 'z') {
value = generalField[i] - 7;
binaryAppend(binaryString, value, 7);
}
last_mode = EncodeMode.ISOIEC;
if (generalField[i] == FNC1) {
binaryString.append("01111");
// TODO: FNC1 should act as an implicit numeric latch, so the commented out
// line below should be correct, but ZXing cannot
// read barcodes which use FNC1 as an implicit numeric latch... so for now,
// and in order to achieve widest compatibility,
// we waste 3 bits and don't perform the implicit mode change (see
// https://sourceforge.net/p/zint/tickets/145/)
// last_mode = EncodeMode.NUMERIC;
} /* FNC1 / Numeric latch */
if (generalField[i] == '!') {
binaryString.append("11101000"); /* exclamation mark */
}
if (generalField[i] == 34) {
binaryString.append("11101001"); /* quotation mark */
}
if (generalField[i] == 37) {
binaryString.append("11101010"); /* percent sign */
}
if (generalField[i] == '&') {
binaryString.append("11101011"); /* ampersand */
}
if (generalField[i] == 39) {
binaryString.append("11101100"); /* apostrophe */
}
if (generalField[i] == '(') {
binaryString.append("11101101"); /* left parenthesis */
}
if (generalField[i] == ')') {
binaryString.append("11101110"); /* right parenthesis */
}
if (generalField[i] == '*') {
binaryString.append("11101111"); /* asterisk */
}
if (generalField[i] == '+') {
binaryString.append("11110000"); /* plus sign */
}
if (generalField[i] == ',') {
binaryString.append("11110001"); /* comma */
}
if (generalField[i] == '-') {
binaryString.append("11110010"); /* minus or hyphen */
}
if (generalField[i] == '.') {
binaryString.append("11110011"); /* period or full stop */
}
if (generalField[i] == '/') {
binaryString.append("11110100"); /* slash or solidus */
}
if (generalField[i] == ':') {
binaryString.append("11110101"); /* colon */
}
if (generalField[i] == ';') {
binaryString.append("11110110"); /* semicolon */
}
if (generalField[i] == '<') {
binaryString.append("11110111"); /* less-than sign */
}
if (generalField[i] == '=') {
binaryString.append("11111000"); /* equals sign */
}
if (generalField[i] == '>') {
binaryString.append("11111001"); /* greater-than sign */
}
if (generalField[i] == '?') {
binaryString.append("11111010"); /* question mark */
}
if (generalField[i] == '_') {
binaryString.append("11111011"); /* underline or low line */
}
if (generalField[i] == ' ') {
binaryString.append("11111100"); /* space */
}
i++;
break;
}
 
current_length = i;
if (latch) {
current_length++;
}
 
} while (current_length < generalField.length);
 
remainder = calculateRemainder(binaryString.length());
 
if (latch) {
/* There is still one more numeric digit to encode */
if (last_mode == EncodeMode.NUMERIC) {
if (remainder >= 4 && remainder <= 6) {
value = generalField[i] - '0';
value++;
binaryAppend(binaryString, value, 4);
} else {
d1 = generalField[i] - '0';
d2 = 10;
value = 11 * d1 + d2 + 8;
binaryAppend(binaryString, value, 7);
}
} else {
value = generalField[i] - 43;
binaryAppend(binaryString, value, 5);
}
}
}
 
if (binaryString.length() > 252) {
throw new OkapiException("Input too long");
}
 
remainder = calculateRemainder(binaryString.length());
 
/* Now add padding to binary string (7.2.5.5.4) */
i = remainder;
if (generalField.length != 0 && last_mode == EncodeMode.NUMERIC) {
padstring = "0000";
i -= 4;
} else {
padstring = "";
}
for (; i > 0; i -= 5) {
padstring += "00100";
}
 
binaryString.append(padstring.substring(0, remainder));
 
/* Patch variable length symbol bit field */
char patchEvenOdd, patchSize;
if ((binaryString.length() / 12 + 1 & 1) == 0) {
patchEvenOdd = '0';
} else {
patchEvenOdd = '1';
}
if (binaryString.length() <= 156) {
patchSize = '0';
} else {
patchSize = '1';
}
 
if (encodingMethod == 1) {
binaryString.setCharAt(2, patchEvenOdd);
binaryString.setCharAt(3, patchSize);
}
if (encodingMethod == 2) {
binaryString.setCharAt(3, patchEvenOdd);
binaryString.setCharAt(4, patchSize);
}
if (encodingMethod == 5 || encodingMethod == 6) {
binaryString.setCharAt(6, patchEvenOdd);
binaryString.setCharAt(7, patchSize);
}
 
return encodingMethod;
}
 
private static int calculateRemainder(final int binaryStringLength) {
int remainder = 12 - binaryStringLength % 12;
if (remainder == 12) {
remainder = 0;
}
if (binaryStringLength < 36) {
remainder = 36 - binaryStringLength;
}
return remainder;
}
 
/** Logs binary string as hexadecimal */
private void logBinaryStringInfo(final StringBuilder binaryString) {
 
infoLine("Binary Length: " + binaryString.length());
info("Binary String: ");
 
int nibble = 0;
for (int i = 0; i < binaryString.length(); i++) {
switch (i % 4) {
case 0:
if (binaryString.charAt(i) == '1') {
nibble += 8;
}
break;
case 1:
if (binaryString.charAt(i) == '1') {
nibble += 4;
}
break;
case 2:
if (binaryString.charAt(i) == '1') {
nibble += 2;
}
break;
case 3:
if (binaryString.charAt(i) == '1') {
nibble += 1;
}
info(Integer.toHexString(nibble));
nibble = 0;
break;
}
}
 
if (binaryString.length() % 4 != 0) {
info(Integer.toHexString(nibble));
}
 
infoLine();
}
 
/**
* Attempts to apply encoding rules from sections 7.2.5.5.1 to 7.2.5.5.3 of ISO/IEC 24724:2006
*/
private static boolean applyGeneralFieldRules(final EncodeMode[] generalFieldType) {
 
int block_count, i, j, k;
EncodeMode current, next, last;
final int[] blockLength = new int[200];
final EncodeMode[] blockType = new EncodeMode[200];
 
block_count = 0;
 
blockLength[block_count] = 1;
blockType[block_count] = generalFieldType[0];
 
for (i = 1; i < generalFieldType.length; i++) {
current = generalFieldType[i];
last = generalFieldType[i - 1];
 
if (current == last) {
blockLength[block_count] = blockLength[block_count] + 1;
} else {
block_count++;
blockLength[block_count] = 1;
blockType[block_count] = generalFieldType[i];
}
}
 
block_count++;
 
for (i = 0; i < block_count; i++) {
current = blockType[i];
next = blockType[i + 1];
 
if (current == EncodeMode.ISOIEC && i != block_count - 1) {
if (next == EncodeMode.ANY_ENC && blockLength[i + 1] >= 4) {
blockType[i + 1] = EncodeMode.NUMERIC;
}
if (next == EncodeMode.ANY_ENC && blockLength[i + 1] < 4) {
blockType[i + 1] = EncodeMode.ISOIEC;
}
if (next == EncodeMode.ALPHA_OR_ISO && blockLength[i + 1] >= 5) {
blockType[i + 1] = EncodeMode.ALPHA;
}
if (next == EncodeMode.ALPHA_OR_ISO && blockLength[i + 1] < 5) {
blockType[i + 1] = EncodeMode.ISOIEC;
}
}
 
if (current == EncodeMode.ALPHA_OR_ISO) {
blockType[i] = EncodeMode.ALPHA;
current = EncodeMode.ALPHA;
}
 
if (current == EncodeMode.ALPHA && i != block_count - 1) {
if (next == EncodeMode.ANY_ENC && blockLength[i + 1] >= 6) {
blockType[i + 1] = EncodeMode.NUMERIC;
}
if (next == EncodeMode.ANY_ENC && blockLength[i + 1] < 6) {
if (i == block_count - 2 && blockLength[i + 1] >= 4) {
blockType[i + 1] = EncodeMode.NUMERIC;
} else {
blockType[i + 1] = EncodeMode.ALPHA;
}
}
}
 
if (current == EncodeMode.ANY_ENC) {
blockType[i] = EncodeMode.NUMERIC;
}
}
 
if (block_count > 1) {
i = 1;
while (i < block_count) {
if (blockType[i - 1] == blockType[i]) {
/* bring together */
blockLength[i - 1] = blockLength[i - 1] + blockLength[i];
j = i + 1;
 
/* decrease the list */
while (j < block_count) {
blockLength[j - 1] = blockLength[j];
blockType[j - 1] = blockType[j];
j++;
}
block_count--;
i--;
}
i++;
}
}
 
for (i = 0; i < block_count - 1; i++) {
if (blockType[i] == EncodeMode.NUMERIC && (blockLength[i] & 1) != 0) {
/* Odd size numeric block */
blockLength[i] = blockLength[i] - 1;
blockLength[i + 1] = blockLength[i + 1] + 1;
}
}
 
j = 0;
for (i = 0; i < block_count; i++) {
for (k = 0; k < blockLength[i]; k++) {
generalFieldType[j] = blockType[i];
j++;
}
}
 
if (blockType[block_count - 1] == EncodeMode.NUMERIC && (blockLength[block_count - 1] & 1) != 0) {
/*
* If the last block is numeric and an odd size, further processing needs to be done
* outside this procedure
*/
return true;
} else {
return false;
}
}
 
private static int parseInt(final int[] chars, final int index, final int length) {
int val = 0;
int pow = (int) Math.pow(10, length - 1);
for (int i = 0; i < length; i++) {
final int c = chars[index + i];
val += (c - '0') * pow;
pow /= 10;
}
return val;
}
}
/trunk/Modules/Module Label/src/uk/org/okapibarcode/backend/MicroQrCode.java
New file
0,0 → 1,1606
/*
* 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.io.UnsupportedEncodingException;
 
/**
* Implements Micro QR Code According to ISO/IEC 18004:2006 <br>
* A miniature version of the QR Code symbol for short messages. QR Code symbols can encode
* characters in the Latin-1 set and Kanji characters which are members of the Shift-JIS encoding
* scheme.
*
* @author <a href="mailto:rstuart114@gmail.com">Robin Stuart</a>
*/
public class MicroQrCode extends Symbol {
 
public enum EccMode {
L, M, Q, H
}
 
private enum qrMode {
NULL, KANJI, BINARY, ALPHANUM, NUMERIC
}
 
/* Table 5 - Encoding/Decoding table for Alphanumeric mode */
private static final char[] RHODIUM = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' };
 
private static final int[] QR_ANNEX_C1 = {
/* Micro QR Code format information */
0x4445, 0x4172, 0x4e2b, 0x4b1c, 0x55ae, 0x5099, 0x5fc0, 0x5af7, 0x6793, 0x62a4, 0x6dfd, 0x68ca, 0x7678, 0x734f, 0x7c16, 0x7921, 0x06de, 0x03e9, 0x0cb0, 0x0987, 0x1735, 0x1202, 0x1d5b,
0x186c, 0x2508, 0x203f, 0x2f66, 0x2a51, 0x34e3, 0x31d4, 0x3e8d, 0x3bba };
 
private static final int[] MICRO_QR_SIZES = { 11, 13, 15, 17 };
 
// user-specified values and settings
 
private int preferredVersion;
private EccMode preferredEccLevel = EccMode.L;
 
// internal state calculated when setContent() is called
 
private qrMode[] inputMode;
private StringBuilder binary;
private final int[] binaryCount = new int[4];
private int[] grid;
private int[] eval;
 
/**
* <p>
* Sets the preferred symbol size. This value may be ignored if the data string is too large to
* fit into the specified symbol. Input values correspond to symbol sizes as shown in the
* following table.
*
* <table summary="Range of Micro QR symbol sizes">
* <tbody>
* <tr>
* <th>Input</th>
* <th>Version</th>
* <th>Symbol Size</th>
* </tr>
* <tr>
* <td>1</td>
* <td>M1</td>
* <td>11 x 11</td>
* </tr>
* <tr>
* <td>2</td>
* <td>M2</td>
* <td>13 x 13</td>
* </tr>
* <tr>
* <td>3</td>
* <td>M3</td>
* <td>15 x 15</td>
* </tr>
* <tr>
* <td>4</td>
* <td>M4</td>
* <td>17 x 17</td>
* </tr>
* </tbody>
* </table>
*
* @param version symbol size
*/
public void setPreferredVersion(final int version) {