Skip to content

Commit ba96033

Browse files
JonPReillySamuel Huylebroeck
authored andcommitted
Added MSI Plessey Barcode Support
Added MSI Plessey Barcode Support Implemented createAwtImage Added tests Added javadocs DEVSIX-1580
1 parent 17dc24d commit ba96033

File tree

5 files changed

+482
-0
lines changed

5 files changed

+482
-0
lines changed

barcodes/src/main/java/com/itextpdf/barcodes/Barcode1D.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ public abstract class Barcode1D {
5858
public static final int ALIGN_RIGHT = 2;
5959
public static final int ALIGN_CENTER = 3;
6060

61+
/**
62+
* The default color to draw if a bar is present.
63+
*/
64+
protected final java.awt.Color DEFAULT_BAR_FOREGROUND_COLOR = java.awt.Color.BLACK;
65+
/**
66+
* The default color to draw if a bar is not present.
67+
*/
68+
protected final java.awt.Color DEFAULT_BAR_BACKGROUND_COLOR = java.awt.Color.WHITE;
69+
6170
protected PdfDocument document;
6271

6372
/**
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2017 iText Group NV
4+
Authors: iText Software.
5+
6+
This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU Affero General Public License version 3
8+
as published by the Free Software Foundation with the addition of the
9+
following permission added to Section 15 as permitted in Section 7(a):
10+
FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
11+
ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
12+
OF THIRD PARTY RIGHTS
13+
14+
This program is distributed in the hope that it will be useful, but
15+
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16+
or FITNESS FOR A PARTICULAR PURPOSE.
17+
See the GNU Affero General Public License for more details.
18+
You should have received a copy of the GNU Affero General Public License
19+
along with this program; if not, see http://www.gnu.org/licenses or write to
20+
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21+
Boston, MA, 02110-1301 USA, or download the license from the following URL:
22+
http://itextpdf.com/terms-of-use/
23+
24+
The interactive user interfaces in modified source and object code versions
25+
of this program must display Appropriate Legal Notices, as required under
26+
Section 5 of the GNU Affero General Public License.
27+
28+
In accordance with Section 7(b) of the GNU Affero General Public License,
29+
a covered work must retain the producer line in every PDF that is created
30+
or manipulated using iText.
31+
32+
You can be released from the requirements of the license by purchasing
33+
a commercial license. Buying such a license is mandatory as soon as you
34+
develop commercial activities involving the iText software without
35+
disclosing the source code of your own applications.
36+
These activities include: offering paid services to customers as an ASP,
37+
serving PDFs on the fly in a web application, shipping iText with a closed
38+
source product.
39+
40+
For more information, please contact iText Software Corp. at this
41+
42+
*/
43+
package com.itextpdf.barcodes;
44+
45+
import com.itextpdf.kernel.color.Color;
46+
import com.itextpdf.kernel.geom.Rectangle;
47+
import com.itextpdf.kernel.pdf.PdfDocument;
48+
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
49+
50+
import java.awt.Image;
51+
52+
/**
53+
* Implements the MSI Barcode.
54+
* The <CODE>code</CODE> may only contain numeric characters.
55+
* The {@link #getChecksum(String) getChecksum} method returns the mod 10 checksum digit which is the most widely used for MSI barcodes.
56+
*/
57+
public class BarcodeMSI extends Barcode1D {
58+
/**
59+
* The index chars to <CODE>BARS</CODE> representing valid characters in the <CODE>code</CODE>
60+
*/
61+
private static final String CHARS = "0123456789";
62+
63+
/**
64+
* The sequence prepended to the start of all MSI Barcodes.
65+
*/
66+
private static final byte[] BARS_START = new byte[]{1, 1, 0};
67+
68+
/**
69+
* The sequence appended to the end of all MSI Barcodes.
70+
*/
71+
private static final byte[] BARS_END = new byte[]{1, 0, 0, 1};
72+
73+
/**
74+
* The bars to generate the code.
75+
*/
76+
private static final byte[][] BARS = new byte[][]{
77+
{1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, // 0
78+
{1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0}, // 1
79+
{1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0}, // 2
80+
{1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0}, // 3
81+
{1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0}, // 4
82+
{1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0}, // 5
83+
{1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0}, // 6
84+
{1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0}, // 7
85+
{1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, // 8
86+
{1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0} // 9
87+
};
88+
89+
/**
90+
* The number of individual bars either drawn or not drawn per character of the <CODE>code</CODE>
91+
*/
92+
private static final int BARS_PER_CHARACTER = 12;
93+
94+
/**
95+
* Creates a new BarcodeMSI
96+
*/
97+
protected BarcodeMSI(PdfDocument document) {
98+
super(document);
99+
this.x = 0.8f;
100+
this.n = 2.0f;
101+
this.font = document.getDefaultFont();
102+
this.size = 8.0f;
103+
this.baseline = this.size;
104+
this.barHeight = this.size * 3.0f;
105+
this.generateChecksum = false;
106+
this.checksumText = false;
107+
}
108+
109+
/**
110+
* Gets the maximum area that the barcode and the text, if
111+
* any, will occupy. The lower left corner is always (0, 0).
112+
*
113+
* @return the size the barcode occupies.
114+
*/
115+
@Override
116+
public Rectangle getBarcodeSize() {
117+
float fontX = 0.0f;
118+
float fontY = 0.0f;
119+
String fCode = this.code;
120+
if (this.font != null) {
121+
if (this.baseline > 0.0f) {
122+
fontY = this.baseline - this.getDescender();
123+
} else {
124+
fontY = -this.baseline + this.size;
125+
}
126+
String fullCode = this.code;
127+
fontX = this.font.getWidth(this.altText != null ? this.altText : fullCode, this.size);
128+
}
129+
130+
int len = fCode.length() + 2;
131+
if (this.generateChecksum) {
132+
++len;
133+
}
134+
135+
float fullWidth = (float) len * (6.0f * this.x + 3.0f * this.x * this.n) + (float) (len - 1) * this.x;
136+
fullWidth = Math.max(fullWidth, fontX);
137+
float fullHeight = this.barHeight + fontY;
138+
return new Rectangle(fullWidth, fullHeight);
139+
}
140+
141+
/**
142+
* Places the barcode in a <CODE>PdfCanvas</CODE>. The
143+
* barcode is always placed at coordinates (0, 0). Use the
144+
* translation matrix to move it elsewhere.<p>
145+
* The bars and text are written in the following colors:<p>
146+
* <P><TABLE BORDER=1>
147+
* <TR>
148+
* <TH><P><CODE>barColor</CODE></TH>
149+
* <TH><P><CODE>textColor</CODE></TH>
150+
* <TH><P>Result</TH>
151+
* </TR>
152+
* <TR>
153+
* <TD><P><CODE>null</CODE></TD>
154+
* <TD><P><CODE>null</CODE></TD>
155+
* <TD><P>bars and text painted with current fill color</TD>
156+
* </TR>
157+
* <TR>
158+
* <TD><P><CODE>barColor</CODE></TD>
159+
* <TD><P><CODE>null</CODE></TD>
160+
* <TD><P>bars and text painted with <CODE>barColor</CODE></TD>
161+
* </TR>
162+
* <TR>
163+
* <TD><P><CODE>null</CODE></TD>
164+
* <TD><P><CODE>textColor</CODE></TD>
165+
* <TD><P>bars painted with current color<br>text painted with <CODE>textColor</CODE></TD>
166+
* </TR>
167+
* <TR>
168+
* <TD><P><CODE>barColor</CODE></TD>
169+
* <TD><P><CODE>textColor</CODE></TD>
170+
* <TD><P>bars painted with <CODE>barColor</CODE><br>text painted with <CODE>textColor</CODE></TD>
171+
* </TR>
172+
* </TABLE>
173+
*
174+
* @param canvas the <CODE>PdfCanvas</CODE> where the barcode will be placed
175+
* @param barColor the color of the bars. It can be <CODE>null</CODE>
176+
* @param textColor the color of the text. It can be <CODE>null</CODE>
177+
* @return the dimensions the barcode occupies
178+
*/
179+
@Override
180+
public Rectangle placeBarcode(PdfCanvas canvas, Color barColor, Color textColor) {
181+
String fullCode = this.code;
182+
if (this.checksumText) {
183+
fullCode = fullCode + Integer.toString(getChecksum(this.code));
184+
}
185+
float fontX = 0.0f;
186+
if (this.font != null) {
187+
String var10001 = this.altText != null ? this.altText : fullCode;
188+
fullCode = this.altText != null ? this.altText : fullCode;
189+
fontX = this.font.getWidth(var10001, this.size);
190+
}
191+
192+
String bCode = this.code;
193+
if (this.generateChecksum) {
194+
bCode += getChecksum(bCode);
195+
}
196+
int idx;
197+
idx = bCode.length();
198+
float fullWidth = (float) ((idx + 2) * 11) * this.x + 2.0f * this.x;
199+
float barStartX = 0.0f;
200+
float textStartX = 0.0f;
201+
switch (this.textAlignment) {
202+
case 1:
203+
break;
204+
case 2:
205+
if (fontX > fullWidth) {
206+
barStartX = fontX - fullWidth;
207+
} else {
208+
textStartX = fullWidth - fontX;
209+
}
210+
break;
211+
default:
212+
if (fontX > fullWidth) {
213+
barStartX = (fontX - fullWidth) / 2.0f;
214+
} else {
215+
textStartX = (fullWidth - fontX) / 2.0f;
216+
}
217+
}
218+
219+
float barStartY = 0.0f;
220+
float textStartY = 0.0f;
221+
if (this.font != null) {
222+
if (this.baseline <= 0.0f) {
223+
textStartY = this.barHeight - this.baseline;
224+
} else {
225+
textStartY = -this.getDescender();
226+
barStartY = textStartY + this.baseline;
227+
}
228+
}
229+
byte[] bars = getBarsMSI(bCode);
230+
if (barColor != null) {
231+
canvas.setFillColor(barColor);
232+
}
233+
for (int k = 0; k < bars.length; ++k) {
234+
float w = (float) bars[k] * this.x;
235+
if (bars[k] == 1)
236+
canvas.rectangle((double) barStartX, (double) barStartY, (double) (w - this.inkSpreading), (double) this.barHeight);
237+
barStartX += this.x;
238+
}
239+
canvas.fill();
240+
if (this.font != null) {
241+
if (textColor != null) {
242+
canvas.setFillColor(textColor);
243+
}
244+
canvas.beginText();
245+
canvas.setFontAndSize(this.font, this.size);
246+
canvas.setTextMatrix(textStartX, textStartY);
247+
canvas.showText(fullCode);
248+
canvas.endText();
249+
}
250+
return this.getBarcodeSize();
251+
}
252+
253+
/**
254+
* Creates a <CODE>java.awt.Image</CODE>. This image only
255+
* contains the bars without any text.
256+
*
257+
* @param foreground the color of the bars
258+
* @param background the color of the background
259+
* @return the image
260+
*/
261+
@Override
262+
public Image createAwtImage(java.awt.Color foreground, java.awt.Color background) {
263+
int foregroundColor;
264+
int backgroundColor;
265+
if (foreground == null) {
266+
foregroundColor = DEFAULT_BAR_FOREGROUND_COLOR.getRGB();
267+
} else {
268+
foregroundColor = foreground.getRGB();
269+
}
270+
271+
if (background == null) {
272+
backgroundColor = DEFAULT_BAR_BACKGROUND_COLOR.getRGB();
273+
} else {
274+
backgroundColor = background.getRGB();
275+
}
276+
277+
java.awt.Canvas canvas = new java.awt.Canvas();
278+
String bCode = this.code;
279+
if (this.generateChecksum) {
280+
bCode = bCode + Integer.toString(getChecksum(this.code));
281+
}
282+
283+
byte[] bars = getBarsMSI(bCode);
284+
int fullWidth = bars.length;
285+
int fullHeight = (int) this.barHeight;
286+
int[] pix = new int[fullWidth * fullHeight];
287+
288+
for (int x = 0; x < bars.length; x++) {
289+
int color = (bars[x] == 1 ? foregroundColor : backgroundColor);
290+
for (int y = 0; y < fullHeight; y++) {
291+
int currentPixel = x + (y * fullWidth);
292+
pix[currentPixel] = color;
293+
}
294+
}
295+
return canvas.createImage(new java.awt.image.MemoryImageSource(fullWidth, fullHeight, pix, 0, fullWidth));
296+
}
297+
298+
/**
299+
* Creates the bars.
300+
*
301+
* @param text the text to create the bars.
302+
* @return the bars
303+
*/
304+
public static byte[] getBarsMSI(String text) {
305+
if (text == null) {
306+
throw new IllegalArgumentException("Valid code required to generate MSI barcode.");
307+
}
308+
byte[] bars = new byte[((text.length()) * BARS_PER_CHARACTER) + 7];
309+
System.arraycopy(BARS_START, 0, bars, 0, 3);
310+
for (int x = 0; x < text.length(); x++) {
311+
char ch = text.charAt(x);
312+
int idx = CHARS.indexOf(ch);
313+
if (idx < 0) {
314+
throw new IllegalArgumentException("The character " + text.charAt(x) + " is illegal in MSI bar codes.");
315+
}
316+
System.arraycopy(BARS[idx], 0, bars, 3 + x * 12, 12);
317+
}
318+
System.arraycopy(BARS_END, 0, bars, bars.length - 4, 4);
319+
return bars;
320+
}
321+
322+
/**
323+
* Calculates the mod 10 checksum digit using the Luhn algorithm.
324+
*
325+
* @param text the barcode data
326+
* @return the checksum digit
327+
*/
328+
public static int getChecksum(String text) {
329+
if (text == null) {
330+
throw new IllegalArgumentException("Valid code required to generate checksum for MSI barcode");
331+
}
332+
String[] strArray = text.split("");
333+
int[] digits = new int[strArray.length];
334+
for (int i = 0; i < strArray.length; i++) {
335+
try {
336+
digits[i] = Integer.parseInt(strArray[i]);
337+
} catch (NumberFormatException e) {
338+
throw new IllegalArgumentException("The character " + text.charAt(i) + " is illegal in MSI bar codes.");
339+
}
340+
}
341+
int sum = 0;
342+
int length = digits.length;
343+
for (int i = 0; i < length; i++) {
344+
int digit = digits[length - i - 1];
345+
if (i % 2 == 0) {
346+
digit *= 2;
347+
}
348+
sum += digit > 9 ? digit - 9 : digit;
349+
}
350+
return (sum * 9) % 10;
351+
}
352+
}

0 commit comments

Comments
 (0)