View Javadoc

1   /***********************************************************************************************************************
2   Copyright (c) 2003, International Barcode Consortium
3   All rights reserved.
4   
5   Redistribution and use in source and binary forms, with or without modification,
6   are permitted provided that the following conditions are met:
7   
8       * Redistributions of source code must retain the above copyright notice, this list of
9         conditions and the following disclaimer.
10      * Redistributions in binary form must reproduce the above copyright notice, this list of
11        conditions and the following disclaimer in the documentation and/or other materials
12        provided with the distribution.
13      * Neither the name of the International Barcode Consortium nor the names of any contributors may be used to endorse
14        or promote products derived from this software without specific prior written permission.
15  
16  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
17  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  POSSIBILITY OF SUCH DAMAGE.
25  ***********************************************************************************************************************/
26  
27  package net.sourceforge.barbecue.twod.pdf417;
28  
29  import net.sourceforge.barbecue.Module;
30  import net.sourceforge.barbecue.output.Output;
31  import net.sourceforge.barbecue.output.OutputException;
32  
33  /**
34   * Specific module implementation that draws an entire PDF417 barcode
35   * as one barbecue module. This is not an ideal implementation, but was
36   * the best way of integrating the PDF417 code short of re-writing it.
37   *
38   * <p/>Contributed by Alex Ferrer <alex@ftconsult.com>
39   *
40   * @author Alex Ferrer
41   * @author <a href="mailto:opensource@ianbourke.com">Ian Bourke</a>
42   *
43   * @todo Do we really want to fix the DATACOLS to 12?
44   */
45  public class PDF417Module extends Module {
46  	private static final int DATACOLS = 12;
47  
48  	private final String data;
49  	private int[] out;
50  	private int outlen;
51  	private int outrows;
52  	private int col = 0;
53  	private int xp;
54  	private int yp;
55  	private int startX;
56  	private int wsize = 0;
57  	private int barWidth;
58  
59  	/**
60  	 * Constructs the PDF417 barcode with the specified data.
61  	 * @param data The data to encode
62  	 */
63  	public PDF417Module(String data) {
64  		super(new int[0]);
65  		this.data = data;
66  	}
67  
68  	/**
69  	 * Returns the barcode width;
70  	 * @return The barcode width
71  	 */
72  	private int getBarcodeWidth() {
73  		return wsize - startX;
74  	}
75  
76  	/**
77  	 * Returns the barcode height.
78  	 * @return The barcode height
79  	 */
80  	int getBarcodeHeight() {
81  		return yp;
82  	}
83  
84  	/**
85  	 * Draw the barcode to the specified outputter, at the specified origin.
86  	 * @param outputter The outputter
87  	 * @param x The X component of the origin
88  	 * @param y The Y component of the origin
89  	 * @param barWidth
90  	 * @param barHeight
91  	 * @return The total width drawn
92  	 */
93  	protected int draw(Output outputter, int x, int y, int barWidth, int barHeight) throws OutputException {
94  		this.xp = (int) x;
95  		this.startX = (int) x;
96  		this.yp = (int) y;
97  		this.barWidth = (int) barWidth;
98  		createCodewords(data.toCharArray(), data.length());
99  		createBits(out, outlen, outrows);
100 		encode(out, outrows, outputter);
101 
102 		return getBarcodeWidth();
103 	}
104 
105 	/**
106 	 * I have no idea what this does.
107 	 * @param data The barcode data
108 	 * @param length The length of the data
109 	 * @param ecLength The length of the EC (2)
110 	 */
111 	private void generateEC(int[] data, int length, int ecLength) {
112 		int b0 = 0;
113 		int b1 = 0;
114 		int g0 = 27;
115 		int g1 = 917;  /* (x-3)(x-9) = x^2+917x+27 mod 929 */
116 
117 		/* Initialize */
118 		data[length] = 0;
119 		data[length + 1] = 0;
120 
121 		/* We only know ecLength == 2 for now */
122 		if (ecLength != 2) {
123 			return;
124 		}
125 
126 		/* Load up with data */
127 		for (int i = 0; i < length; ++i) {
128 			int wrap = (b1 + data[i]) % 929;
129 
130 			if (wrap != 0) {
131 				wrap = 929 - wrap;
132 			}
133 
134 			b1 = (b0 + g1 * wrap) % 929;
135 			b0 = (0 + g0 * wrap) % 929;
136 		}
137 
138 		/* Read off the info */
139 		if (b0 != 0) {
140 			b0 = 929 - b0;
141 		}
142 
143 		if (b1 != 0) {
144 			b1 = 929 - b1;
145 		}
146 
147 		data[length] = b1;
148 		data[length + 1] = b0;
149 	}
150 
151 	private void outbit(int bit, Output params) throws OutputException {
152 		params.drawBar(xp, yp, 1, 1, bit == 1);
153 
154 		xp = xp + barWidth;
155 		if (col++ == wsize - 1) {
156 			col = 0;
157 			yp = yp + 1;
158 			xp = startX;
159 		}
160 	}
161 
162 	private void createCodewords(char[] data, int len) {
163 		int ecLength = 2; /* Number of codewords for error correction */
164 		/* Check args */
165 
166 		if (DATACOLS < 1 || DATACOLS > 30) {
167 			return;
168 		}
169 		/* Calculate the length of the eventual sequence */
170 		outlen = 2 + (len / 6) * 5 + (len % 6) + ecLength;
171 
172 		/* Pad to an integer number of rows, at least 3 */
173 		outrows = outlen / DATACOLS;
174 		if ((outlen % DATACOLS) != 0) {
175 			++outrows;
176 		}
177 		if (outrows < 3) {
178 			outrows = 3;
179 		}
180 		if (outrows > 90) {
181 			return;
182 		}
183 		outlen = outrows * DATACOLS;
184 		/* We don't do multipart symbols (Macro PDF 417) */
185 		if (outlen > 928) {
186 			return;
187 		}
188 
189 		/* The first two codewords are the length and the BC mode latch
190 		   The mode latch is 924 if len is a multiple of 6, 901 otherwise */
191 		out = new int[outlen];              // dimension the array
192 		out[0] = 2 + (len / 6) * 5 + (len % 6);   // 1st value s size of sequence
193 		if (len % 6 != 0) {
194 			out[1] = 901;  					// if len not a multiple of 6
195 		} else {
196 			out[1] = 924;                    // if len *is* a multiple of 6
197 		}
198 
199 		/* Map blocks of 6 bytes to block of 5 codewords */
200 		int inp = 0;
201 		int outp = 2;
202 		while (inp + 5 < len) {
203 			/* Treat the 6 bytes as a big-endian base 256 number */
204 			long codeval = 0;
205 			for (int i = 0; i < 6; ++i) {
206 				codeval <<= 8;
207 				codeval += data[inp++];
208 			}
209 			/* Convert the number to base 900 */
210 			for (int i = 0; i < 5; i++) {
211 				out[outp + 4 - i] = new Long(codeval % 900).intValue();
212 				codeval /= 900;
213 			}
214 			outp += 5;
215 		}
216 
217 		/* Finish up the data */
218 		while (inp < len) {
219 			out[outp++] = data[inp++];
220 		}
221 
222 		/* Do padding */
223 		while (outp < outlen - ecLength) {
224 			out[outp++] = 900;
225 		}
226 
227 		generateEC(out, outp, ecLength);
228 	}
229 
230 	private void createBits(int[] codes, int codelen, int datarows) {
231 		int row, inp, outp;
232 		if (DATACOLS < 1 || DATACOLS > 30
233 		 				 || datarows < 3 || datarows > 90
234 		 				 || codelen != DATACOLS * datarows) {
235 			return;
236 		}
237 		/* Each row has start, left, data, right, stop */
238 		int outlen = datarows * (DATACOLS + 4);
239 		int[] out = new int[outlen];
240 		outp = 0;
241 		inp = 0;
242 
243 		for (row = 0; row < datarows; ++row) {
244 			/* Do each row */
245 			int v = DATACOLS - 1;
246 			int w = row % 3;
247 			int x = row / 3;
248 			int y = datarows / 3;
249 			int z = 0 * 3 + datarows % 3;  /* The 0 is the error correction level */
250 			out[outp++] = PDF417Data.PDF417_START;
251 			switch (w) {
252 				case 0:
253 					out[outp++] = PDF417Data.PDF417_BITS[w][30 * x + y];
254 					break;
255 				case 1:
256 					out[outp++] = PDF417Data.PDF417_BITS[w][30 * x + z];
257 					break;
258 				case 2:
259 					out[outp++] = PDF417Data.PDF417_BITS[w][30 * x + v];
260 					break;
261 			}
262 			for (int i = 0; i < DATACOLS; ++i) {
263 				out[outp++] = PDF417Data.PDF417_BITS[w][codes[inp++]];
264 			}
265 			switch (w) {
266 				case 0:
267 					out[outp++] = PDF417Data.PDF417_BITS[w][30 * x + v];
268 					break;
269 				case 1:
270 					out[outp++] = PDF417Data.PDF417_BITS[w][30 * x + y];
271 					break;
272 				case 2:
273 					out[outp++] = PDF417Data.PDF417_BITS[w][30 * x + z];
274 					break;
275 			}
276 			out[outp++] = PDF417Data.PDF417_STOP;
277 		}
278 		this.out = out;
279 		this.outlen = outlen;
280 	}
281 
282 	private void encode(int[] data, int datarows, Output params) throws OutputException {
283 		int bitpattern;
284 		int row_height = 7;
285 		int npix = 2;
286 		wsize = ((DATACOLS + 4) * 17 + barWidth + 4) * npix;
287 
288 		/* Top quiet zone */
289 		for (int i = 0; i < 2 * npix; i++) {
290 			for (int j = 0; j < ((DATACOLS + 4) * 17 + 1 + 4) * npix; j++) {
291 				outbit(0, params);
292 			}
293 		}
294 
295 		for (int i = 0; i < datarows; i++) {
296 			for (int k = 0; k < row_height; k++) {
297 
298 				/* Left quiet zone */
299 				for (int pixn = 0; pixn < 2 * npix; pixn++) {
300 					outbit(0, params);
301 				}
302 
303 				for (int j = 0; j < (DATACOLS + 4); j++) {
304 					bitpattern = data[(DATACOLS + 4) * i + j];
305 
306 					for (int bitm = 16; bitm >= 0; bitm--) {
307 						for (int pixn = 0; pixn < npix; pixn++) {
308 
309 							if ((bitpattern & (1 << bitm)) != 0) {
310 								outbit(1, params);
311 							} else {
312 								outbit(0, params);
313 							}
314 						}
315 					}
316 				}
317 
318 				for (int pixn = 0; pixn < npix; pixn++) {
319 					outbit(1, params);
320 				}
321 
322 				/* Right quiet zone */
323 				for (int pixn = 0; pixn < 2 * npix; pixn++) {
324 					outbit(0, params);
325 				}
326 			}
327 		}
328 
329 		/* Bottom quiet zone */
330 		for (int i = 0; i < 2 * npix; ++i) {
331 			for (int j = 0; j < ((DATACOLS + 4) * 17 + 1 + 4) * npix; ++j) {
332 				outbit(0, params);
333 			}
334 		}
335 	}
336 }