001/* 002 * $Id: Barcode128.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045import java.awt.Canvas; 046import java.awt.Image; 047import java.awt.image.MemoryImageSource; 048import com.itextpdf.text.error_messages.MessageLocalization; 049 050import com.itextpdf.text.Element; 051import com.itextpdf.text.ExceptionConverter; 052import com.itextpdf.text.Rectangle; 053import com.itextpdf.text.BaseColor; 054 055/** 056 * Implements the code 128 and UCC/EAN-128. Other symbologies are allowed in raw mode.<p> 057 * The code types allowed are:<br> 058 * <ul> 059 * <li><b>CODE128</b> - plain barcode 128. 060 * <li><b>CODE128_UCC</b> - support for UCC/EAN-128 with a full list of AI. 061 * <li><b>CODE128_RAW</b> - raw mode. The code attribute has the actual codes from 0 062 * to 105 followed by '\uffff' and the human readable text. 063 * </ul> 064 * The default parameters are: 065 * <pre> 066 * x = 0.8f; 067 * font = BaseFont.createFont("Helvetica", "winansi", false); 068 * size = 8; 069 * baseline = size; 070 * barHeight = size * 3; 071 * textAlignment = Element.ALIGN_CENTER; 072 * codeType = CODE128; 073 * </pre> 074 * @author Paulo Soares 075 */ 076public class Barcode128 extends Barcode{ 077 078 /** The bars to generate the code. 079 */ 080 private static final byte BARS[][] = 081 { 082 {2, 1, 2, 2, 2, 2}, 083 {2, 2, 2, 1, 2, 2}, 084 {2, 2, 2, 2, 2, 1}, 085 {1, 2, 1, 2, 2, 3}, 086 {1, 2, 1, 3, 2, 2}, 087 {1, 3, 1, 2, 2, 2}, 088 {1, 2, 2, 2, 1, 3}, 089 {1, 2, 2, 3, 1, 2}, 090 {1, 3, 2, 2, 1, 2}, 091 {2, 2, 1, 2, 1, 3}, 092 {2, 2, 1, 3, 1, 2}, 093 {2, 3, 1, 2, 1, 2}, 094 {1, 1, 2, 2, 3, 2}, 095 {1, 2, 2, 1, 3, 2}, 096 {1, 2, 2, 2, 3, 1}, 097 {1, 1, 3, 2, 2, 2}, 098 {1, 2, 3, 1, 2, 2}, 099 {1, 2, 3, 2, 2, 1}, 100 {2, 2, 3, 2, 1, 1}, 101 {2, 2, 1, 1, 3, 2}, 102 {2, 2, 1, 2, 3, 1}, 103 {2, 1, 3, 2, 1, 2}, 104 {2, 2, 3, 1, 1, 2}, 105 {3, 1, 2, 1, 3, 1}, 106 {3, 1, 1, 2, 2, 2}, 107 {3, 2, 1, 1, 2, 2}, 108 {3, 2, 1, 2, 2, 1}, 109 {3, 1, 2, 2, 1, 2}, 110 {3, 2, 2, 1, 1, 2}, 111 {3, 2, 2, 2, 1, 1}, 112 {2, 1, 2, 1, 2, 3}, 113 {2, 1, 2, 3, 2, 1}, 114 {2, 3, 2, 1, 2, 1}, 115 {1, 1, 1, 3, 2, 3}, 116 {1, 3, 1, 1, 2, 3}, 117 {1, 3, 1, 3, 2, 1}, 118 {1, 1, 2, 3, 1, 3}, 119 {1, 3, 2, 1, 1, 3}, 120 {1, 3, 2, 3, 1, 1}, 121 {2, 1, 1, 3, 1, 3}, 122 {2, 3, 1, 1, 1, 3}, 123 {2, 3, 1, 3, 1, 1}, 124 {1, 1, 2, 1, 3, 3}, 125 {1, 1, 2, 3, 3, 1}, 126 {1, 3, 2, 1, 3, 1}, 127 {1, 1, 3, 1, 2, 3}, 128 {1, 1, 3, 3, 2, 1}, 129 {1, 3, 3, 1, 2, 1}, 130 {3, 1, 3, 1, 2, 1}, 131 {2, 1, 1, 3, 3, 1}, 132 {2, 3, 1, 1, 3, 1}, 133 {2, 1, 3, 1, 1, 3}, 134 {2, 1, 3, 3, 1, 1}, 135 {2, 1, 3, 1, 3, 1}, 136 {3, 1, 1, 1, 2, 3}, 137 {3, 1, 1, 3, 2, 1}, 138 {3, 3, 1, 1, 2, 1}, 139 {3, 1, 2, 1, 1, 3}, 140 {3, 1, 2, 3, 1, 1}, 141 {3, 3, 2, 1, 1, 1}, 142 {3, 1, 4, 1, 1, 1}, 143 {2, 2, 1, 4, 1, 1}, 144 {4, 3, 1, 1, 1, 1}, 145 {1, 1, 1, 2, 2, 4}, 146 {1, 1, 1, 4, 2, 2}, 147 {1, 2, 1, 1, 2, 4}, 148 {1, 2, 1, 4, 2, 1}, 149 {1, 4, 1, 1, 2, 2}, 150 {1, 4, 1, 2, 2, 1}, 151 {1, 1, 2, 2, 1, 4}, 152 {1, 1, 2, 4, 1, 2}, 153 {1, 2, 2, 1, 1, 4}, 154 {1, 2, 2, 4, 1, 1}, 155 {1, 4, 2, 1, 1, 2}, 156 {1, 4, 2, 2, 1, 1}, 157 {2, 4, 1, 2, 1, 1}, 158 {2, 2, 1, 1, 1, 4}, 159 {4, 1, 3, 1, 1, 1}, 160 {2, 4, 1, 1, 1, 2}, 161 {1, 3, 4, 1, 1, 1}, 162 {1, 1, 1, 2, 4, 2}, 163 {1, 2, 1, 1, 4, 2}, 164 {1, 2, 1, 2, 4, 1}, 165 {1, 1, 4, 2, 1, 2}, 166 {1, 2, 4, 1, 1, 2}, 167 {1, 2, 4, 2, 1, 1}, 168 {4, 1, 1, 2, 1, 2}, 169 {4, 2, 1, 1, 1, 2}, 170 {4, 2, 1, 2, 1, 1}, 171 {2, 1, 2, 1, 4, 1}, 172 {2, 1, 4, 1, 2, 1}, 173 {4, 1, 2, 1, 2, 1}, 174 {1, 1, 1, 1, 4, 3}, 175 {1, 1, 1, 3, 4, 1}, 176 {1, 3, 1, 1, 4, 1}, 177 {1, 1, 4, 1, 1, 3}, 178 {1, 1, 4, 3, 1, 1}, 179 {4, 1, 1, 1, 1, 3}, 180 {4, 1, 1, 3, 1, 1}, 181 {1, 1, 3, 1, 4, 1}, 182 {1, 1, 4, 1, 3, 1}, 183 {3, 1, 1, 1, 4, 1}, 184 {4, 1, 1, 1, 3, 1}, 185 {2, 1, 1, 4, 1, 2}, 186 {2, 1, 1, 2, 1, 4}, 187 {2, 1, 1, 2, 3, 2} 188 }; 189 190 /** The stop bars. 191 */ 192 private static final byte BARS_STOP[] = {2, 3, 3, 1, 1, 1, 2}; 193 /** The charset code change. 194 */ 195 public static final char CODE_AB_TO_C = 99; 196 /** The charset code change. 197 */ 198 public static final char CODE_AC_TO_B = 100; 199 /** The charset code change. 200 */ 201 public static final char CODE_BC_TO_A = 101; 202 /** The code for UCC/EAN-128. 203 */ 204 public static final char FNC1_INDEX = 102; 205 /** The start code. 206 */ 207 public static final char START_A = 103; 208 /** The start code. 209 */ 210 public static final char START_B = 104; 211 /** The start code. 212 */ 213 public static final char START_C = 105; 214 215 public static final char FNC1 = '\u00ca'; 216 public static final char DEL = '\u00c3'; 217 public static final char FNC3 = '\u00c4'; 218 public static final char FNC2 = '\u00c5'; 219 public static final char SHIFT = '\u00c6'; 220 public static final char CODE_C = '\u00c7'; 221 public static final char CODE_A = '\u00c8'; 222 public static final char FNC4 = '\u00c8'; 223 public static final char STARTA = '\u00cb'; 224 public static final char STARTB = '\u00cc'; 225 public static final char STARTC = '\u00cd'; 226 227 private static final IntHashtable ais = new IntHashtable(); 228 /** Creates new Barcode128 */ 229 public Barcode128() { 230 try { 231 x = 0.8f; 232 font = BaseFont.createFont("Helvetica", "winansi", false); 233 size = 8; 234 baseline = size; 235 barHeight = size * 3; 236 textAlignment = Element.ALIGN_CENTER; 237 codeType = CODE128; 238 } 239 catch (Exception e) { 240 throw new ExceptionConverter(e); 241 } 242 } 243 244 /** 245 * Removes the FNC1 codes in the text. 246 * @param code the text to clean 247 * @return the cleaned text 248 */ 249 public static String removeFNC1(String code) { 250 int len = code.length(); 251 StringBuffer buf = new StringBuffer(len); 252 for (int k = 0; k < len; ++k) { 253 char c = code.charAt(k); 254 if (c >= 32 && c <= 126) 255 buf.append(c); 256 } 257 return buf.toString(); 258 } 259 260 /** 261 * Gets the human readable text of a sequence of AI. 262 * @param code the text 263 * @return the human readable text 264 */ 265 public static String getHumanReadableUCCEAN(String code) { 266 StringBuffer buf = new StringBuffer(); 267 String fnc1 = String.valueOf(FNC1); 268 try { 269 while (true) { 270 if (code.startsWith(fnc1)) { 271 code = code.substring(1); 272 continue; 273 } 274 int n = 0; 275 int idlen = 0; 276 for (int k = 2; k < 5; ++k) { 277 if (code.length() < k) 278 break; 279 if ((n = ais.get(Integer.parseInt(code.substring(0, k)))) != 0) { 280 idlen = k; 281 break; 282 } 283 } 284 if (idlen == 0) 285 break; 286 buf.append('(').append(code.substring(0, idlen)).append(')'); 287 code = code.substring(idlen); 288 if (n > 0) { 289 n -= idlen; 290 if (code.length() <= n) 291 break; 292 buf.append(removeFNC1(code.substring(0, n))); 293 code = code.substring(n); 294 } 295 else { 296 int idx = code.indexOf(FNC1); 297 if (idx < 0) 298 break; 299 buf.append(code.substring(0,idx)); 300 code = code.substring(idx + 1); 301 } 302 } 303 } 304 catch (Exception e) { 305 //empty 306 } 307 buf.append(removeFNC1(code)); 308 return buf.toString(); 309 } 310 311 /** Returns <CODE>true</CODE> if the next <CODE>numDigits</CODE> 312 * starting from index <CODE>textIndex</CODE> are numeric skipping any FNC1. 313 * @param text the text to check 314 * @param textIndex where to check from 315 * @param numDigits the number of digits to check 316 * @return the check result 317 */ 318 static boolean isNextDigits(String text, int textIndex, int numDigits) { 319 int len = text.length(); 320 while (textIndex < len && numDigits > 0) { 321 if (text.charAt(textIndex) == FNC1) { 322 ++textIndex; 323 continue; 324 } 325 int n = Math.min(2, numDigits); 326 if (textIndex + n > len) 327 return false; 328 while (n-- > 0) { 329 char c = text.charAt(textIndex++); 330 if (c < '0' || c > '9') 331 return false; 332 --numDigits; 333 } 334 } 335 return numDigits == 0; 336 } 337 338 /** Packs the digits for charset C also considering FNC1. It assumes that all the parameters 339 * are valid. 340 * @param text the text to pack 341 * @param textIndex where to pack from 342 * @param numDigits the number of digits to pack. It is always an even number 343 * @return the packed digits, two digits per character 344 */ 345 static String getPackedRawDigits(String text, int textIndex, int numDigits) { 346 StringBuilder out = new StringBuilder(""); 347 int start = textIndex; 348 while (numDigits > 0) { 349 if (text.charAt(textIndex) == FNC1) { 350 out.append(FNC1_INDEX); 351 ++textIndex; 352 continue; 353 } 354 numDigits -= 2; 355 int c1 = text.charAt(textIndex++) - '0'; 356 int c2 = text.charAt(textIndex++) - '0'; 357 out.append((char)(c1 * 10 + c2)); 358 } 359 return (char)(textIndex - start) + out.toString(); 360 } 361 362 /** Converts the human readable text to the characters needed to 363 * create a barcode. Some optimization is done to get the shortest code. 364 * @param text the text to convert 365 * @param ucc <CODE>true</CODE> if it is an UCC/EAN-128. In this case 366 * the character FNC1 is added 367 * @return the code ready to be fed to getBarsCode128Raw() 368 */ 369 public static String getRawText(String text, boolean ucc) { 370 String out = ""; 371 int tLen = text.length(); 372 if (tLen == 0) { 373 out += START_B; 374 if (ucc) 375 out += FNC1_INDEX; 376 return out; 377 } 378 int c = 0; 379 for (int k = 0; k < tLen; ++k) { 380 c = text.charAt(k); 381 if (c > 127 && c != FNC1) 382 throw new RuntimeException(MessageLocalization.getComposedMessage("there.are.illegal.characters.for.barcode.128.in.1", text)); 383 } 384 c = text.charAt(0); 385 char currentCode = START_B; 386 int index = 0; 387 if (isNextDigits(text, index, 2)) { 388 currentCode = START_C; 389 out += currentCode; 390 if (ucc) 391 out += FNC1_INDEX; 392 String out2 = getPackedRawDigits(text, index, 2); 393 index += out2.charAt(0); 394 out += out2.substring(1); 395 } 396 else if (c < ' ') { 397 currentCode = START_A; 398 out += currentCode; 399 if (ucc) 400 out += FNC1_INDEX; 401 out += (char)(c + 64); 402 ++index; 403 } 404 else { 405 out += currentCode; 406 if (ucc) 407 out += FNC1_INDEX; 408 if (c == FNC1) 409 out += FNC1_INDEX; 410 else 411 out += (char)(c - ' '); 412 ++index; 413 } 414 while (index < tLen) { 415 switch (currentCode) { 416 case START_A: 417 { 418 if (isNextDigits(text, index, 4)) { 419 currentCode = START_C; 420 out += CODE_AB_TO_C; 421 String out2 = getPackedRawDigits(text, index, 4); 422 index += out2.charAt(0); 423 out += out2.substring(1); 424 } 425 else { 426 c = text.charAt(index++); 427 if (c == FNC1) 428 out += FNC1_INDEX; 429 else if (c > '_') { 430 currentCode = START_B; 431 out += CODE_AC_TO_B; 432 out += (char)(c - ' '); 433 } 434 else if (c < ' ') 435 out += (char)(c + 64); 436 else 437 out += (char)(c - ' '); 438 } 439 } 440 break; 441 case START_B: 442 { 443 if (isNextDigits(text, index, 4)) { 444 currentCode = START_C; 445 out += CODE_AB_TO_C; 446 String out2 = getPackedRawDigits(text, index, 4); 447 index += out2.charAt(0); 448 out += out2.substring(1); 449 } 450 else { 451 c = text.charAt(index++); 452 if (c == FNC1) 453 out += FNC1_INDEX; 454 else if (c < ' ') { 455 currentCode = START_A; 456 out += CODE_BC_TO_A; 457 out += (char)(c + 64); 458 } 459 else { 460 out += (char)(c - ' '); 461 } 462 } 463 } 464 break; 465 case START_C: 466 { 467 if (isNextDigits(text, index, 2)) { 468 String out2 = getPackedRawDigits(text, index, 2); 469 index += out2.charAt(0); 470 out += out2.substring(1); 471 } 472 else { 473 c = text.charAt(index++); 474 if (c == FNC1) 475 out += FNC1_INDEX; 476 else if (c < ' ') { 477 currentCode = START_A; 478 out += CODE_BC_TO_A; 479 out += (char)(c + 64); 480 } 481 else { 482 currentCode = START_B; 483 out += CODE_AC_TO_B; 484 out += (char)(c - ' '); 485 } 486 } 487 } 488 break; 489 } 490 } 491 return out; 492 } 493 494 /** Generates the bars. The input has the actual barcodes, not 495 * the human readable text. 496 * @param text the barcode 497 * @return the bars 498 */ 499 public static byte[] getBarsCode128Raw(String text) { 500 int idx = text.indexOf('\uffff'); 501 if (idx >= 0) 502 text = text.substring(0, idx); 503 int chk = text.charAt(0); 504 for (int k = 1; k < text.length(); ++k) 505 chk += k * text.charAt(k); 506 chk = chk % 103; 507 text += (char)chk; 508 byte bars[] = new byte[(text.length() + 1) * 6 + 7]; 509 int k; 510 for (k = 0; k < text.length(); ++k) 511 System.arraycopy(BARS[text.charAt(k)], 0, bars, k * 6, 6); 512 System.arraycopy(BARS_STOP, 0, bars, k * 6, 7); 513 return bars; 514 } 515 516 /** Gets the maximum area that the barcode and the text, if 517 * any, will occupy. The lower left corner is always (0, 0). 518 * @return the size the barcode occupies. 519 */ 520 public Rectangle getBarcodeSize() { 521 float fontX = 0; 522 float fontY = 0; 523 String fullCode; 524 if (font != null) { 525 if (baseline > 0) 526 fontY = baseline - font.getFontDescriptor(BaseFont.DESCENT, size); 527 else 528 fontY = -baseline + size; 529 if (codeType == CODE128_RAW) { 530 int idx = code.indexOf('\uffff'); 531 if (idx < 0) 532 fullCode = ""; 533 else 534 fullCode = code.substring(idx + 1); 535 } 536 else if (codeType == CODE128_UCC) 537 fullCode = getHumanReadableUCCEAN(code); 538 else 539 fullCode = removeFNC1(code); 540 fontX = font.getWidthPoint(altText != null ? altText : fullCode, size); 541 } 542 if (codeType == CODE128_RAW) { 543 int idx = code.indexOf('\uffff'); 544 if (idx >= 0) 545 fullCode = code.substring(0, idx); 546 else 547 fullCode = code; 548 } 549 else { 550 fullCode = getRawText(code, codeType == CODE128_UCC); 551 } 552 int len = fullCode.length(); 553 float fullWidth = (len + 2) * 11 * x + 2 * x; 554 fullWidth = Math.max(fullWidth, fontX); 555 float fullHeight = barHeight + fontY; 556 return new Rectangle(fullWidth, fullHeight); 557 } 558 559 /** Places the barcode in a <CODE>PdfContentByte</CODE>. The 560 * barcode is always placed at coordinates (0, 0). Use the 561 * translation matrix to move it elsewhere.<p> 562 * The bars and text are written in the following colors:<p> 563 * <P><TABLE BORDER=1> 564 * <TR> 565 * <TH><P><CODE>barColor</CODE></TH> 566 * <TH><P><CODE>textColor</CODE></TH> 567 * <TH><P>Result</TH> 568 * </TR> 569 * <TR> 570 * <TD><P><CODE>null</CODE></TD> 571 * <TD><P><CODE>null</CODE></TD> 572 * <TD><P>bars and text painted with current fill color</TD> 573 * </TR> 574 * <TR> 575 * <TD><P><CODE>barColor</CODE></TD> 576 * <TD><P><CODE>null</CODE></TD> 577 * <TD><P>bars and text painted with <CODE>barColor</CODE></TD> 578 * </TR> 579 * <TR> 580 * <TD><P><CODE>null</CODE></TD> 581 * <TD><P><CODE>textColor</CODE></TD> 582 * <TD><P>bars painted with current color<br>text painted with <CODE>textColor</CODE></TD> 583 * </TR> 584 * <TR> 585 * <TD><P><CODE>barColor</CODE></TD> 586 * <TD><P><CODE>textColor</CODE></TD> 587 * <TD><P>bars painted with <CODE>barColor</CODE><br>text painted with <CODE>textColor</CODE></TD> 588 * </TR> 589 * </TABLE> 590 * @param cb the <CODE>PdfContentByte</CODE> where the barcode will be placed 591 * @param barColor the color of the bars. It can be <CODE>null</CODE> 592 * @param textColor the color of the text. It can be <CODE>null</CODE> 593 * @return the dimensions the barcode occupies 594 */ 595 public Rectangle placeBarcode(PdfContentByte cb, BaseColor barColor, BaseColor textColor) { 596 String fullCode; 597 if (codeType == CODE128_RAW) { 598 int idx = code.indexOf('\uffff'); 599 if (idx < 0) 600 fullCode = ""; 601 else 602 fullCode = code.substring(idx + 1); 603 } 604 else if (codeType == CODE128_UCC) 605 fullCode = getHumanReadableUCCEAN(code); 606 else 607 fullCode = removeFNC1(code); 608 float fontX = 0; 609 if (font != null) { 610 fontX = font.getWidthPoint(fullCode = altText != null ? altText : fullCode, size); 611 } 612 String bCode; 613 if (codeType == CODE128_RAW) { 614 int idx = code.indexOf('\uffff'); 615 if (idx >= 0) 616 bCode = code.substring(0, idx); 617 else 618 bCode = code; 619 } 620 else { 621 bCode = getRawText(code, codeType == CODE128_UCC); 622 } 623 int len = bCode.length(); 624 float fullWidth = (len + 2) * 11 * x + 2 * x; 625 float barStartX = 0; 626 float textStartX = 0; 627 switch (textAlignment) { 628 case Element.ALIGN_LEFT: 629 break; 630 case Element.ALIGN_RIGHT: 631 if (fontX > fullWidth) 632 barStartX = fontX - fullWidth; 633 else 634 textStartX = fullWidth - fontX; 635 break; 636 default: 637 if (fontX > fullWidth) 638 barStartX = (fontX - fullWidth) / 2; 639 else 640 textStartX = (fullWidth - fontX) / 2; 641 break; 642 } 643 float barStartY = 0; 644 float textStartY = 0; 645 if (font != null) { 646 if (baseline <= 0) 647 textStartY = barHeight - baseline; 648 else { 649 textStartY = -font.getFontDescriptor(BaseFont.DESCENT, size); 650 barStartY = textStartY + baseline; 651 } 652 } 653 byte bars[] = getBarsCode128Raw(bCode); 654 boolean print = true; 655 if (barColor != null) 656 cb.setColorFill(barColor); 657 for (int k = 0; k < bars.length; ++k) { 658 float w = bars[k] * x; 659 if (print) 660 cb.rectangle(barStartX, barStartY, w - inkSpreading, barHeight); 661 print = !print; 662 barStartX += w; 663 } 664 cb.fill(); 665 if (font != null) { 666 if (textColor != null) 667 cb.setColorFill(textColor); 668 cb.beginText(); 669 cb.setFontAndSize(font, size); 670 cb.setTextMatrix(textStartX, textStartY); 671 cb.showText(fullCode); 672 cb.endText(); 673 } 674 return getBarcodeSize(); 675 } 676 677 /** Creates a <CODE>java.awt.Image</CODE>. This image only 678 * contains the bars without any text. 679 * @param foreground the color of the bars 680 * @param background the color of the background 681 * @return the image 682 */ 683 public java.awt.Image createAwtImage(java.awt.Color foreground, java.awt.Color background) { 684 int f = foreground.getRGB(); 685 int g = background.getRGB(); 686 Canvas canvas = new Canvas(); 687 String bCode; 688 if (codeType == CODE128_RAW) { 689 int idx = code.indexOf('\uffff'); 690 if (idx >= 0) 691 bCode = code.substring(0, idx); 692 else 693 bCode = code; 694 } 695 else { 696 bCode = getRawText(code, codeType == CODE128_UCC); 697 } 698 int len = bCode.length(); 699 int fullWidth = (len + 2) * 11 + 2; 700 byte bars[] = getBarsCode128Raw(bCode); 701 702 boolean print = true; 703 int ptr = 0; 704 int height = (int)barHeight; 705 int pix[] = new int[fullWidth * height]; 706 for (int k = 0; k < bars.length; ++k) { 707 int w = bars[k]; 708 int c = g; 709 if (print) 710 c = f; 711 print = !print; 712 for (int j = 0; j < w; ++j) 713 pix[ptr++] = c; 714 } 715 for (int k = fullWidth; k < pix.length; k += fullWidth) { 716 System.arraycopy(pix, 0, pix, k, fullWidth); 717 } 718 Image img = canvas.createImage(new MemoryImageSource(fullWidth, height, pix, 0, fullWidth)); 719 720 return img; 721 } 722 723 /** 724 * Sets the code to generate. If it's an UCC code and starts with '(' it will 725 * be split by the AI. This code in UCC mode is valid: 726 * <p> 727 * <code>(01)00000090311314(10)ABC123(15)060916</code> 728 * @param code the code to generate 729 */ 730 public void setCode(String code) { 731 if (getCodeType() == Barcode128.CODE128_UCC && code.startsWith("(")) { 732 int idx = 0; 733 StringBuilder ret = new StringBuilder(""); 734 while (idx >= 0) { 735 int end = code.indexOf(')', idx); 736 if (end < 0) 737 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("badly.formed.ucc.string.1", code)); 738 String sai = code.substring(idx + 1, end); 739 if (sai.length() < 2) 740 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("ai.too.short.1", sai)); 741 int ai = Integer.parseInt(sai); 742 int len = ais.get(ai); 743 if (len == 0) 744 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("ai.not.found.1", sai)); 745 sai = String.valueOf(ai); 746 if (sai.length() == 1) 747 sai = "0" + sai; 748 idx = code.indexOf('(', end); 749 int next = (idx < 0 ? code.length() : idx); 750 ret.append(sai).append(code.substring(end + 1, next)); 751 if (len < 0) { 752 if (idx >= 0) 753 ret.append(FNC1); 754 } 755 else if (next - end - 1 + sai.length() != len) 756 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("invalid.ai.length.1", sai)); 757 } 758 super.setCode(ret.toString()); 759 } 760 else 761 super.setCode(code); 762 } 763 764 static { 765 ais.put(0, 20); 766 ais.put(1, 16); 767 ais.put(2, 16); 768 ais.put(10, -1); 769 ais.put(11, 9); 770 ais.put(12, 8); 771 ais.put(13, 8); 772 ais.put(15, 8); 773 ais.put(17, 8); 774 ais.put(20, 4); 775 ais.put(21, -1); 776 ais.put(22, -1); 777 ais.put(23, -1); 778 ais.put(240, -1); 779 ais.put(241, -1); 780 ais.put(250, -1); 781 ais.put(251, -1); 782 ais.put(252, -1); 783 ais.put(30, -1); 784 for (int k = 3100; k < 3700; ++k) 785 ais.put(k, 10); 786 ais.put(37, -1); 787 for (int k = 3900; k < 3940; ++k) 788 ais.put(k, -1); 789 ais.put(400, -1); 790 ais.put(401, -1); 791 ais.put(402, 20); 792 ais.put(403, -1); 793 for (int k = 410; k < 416; ++k) 794 ais.put(k, 16); 795 ais.put(420, -1); 796 ais.put(421, -1); 797 ais.put(422, 6); 798 ais.put(423, -1); 799 ais.put(424, 6); 800 ais.put(425, 6); 801 ais.put(426, 6); 802 ais.put(7001, 17); 803 ais.put(7002, -1); 804 for (int k = 7030; k < 7040; ++k) 805 ais.put(k, -1); 806 ais.put(8001, 18); 807 ais.put(8002, -1); 808 ais.put(8003, -1); 809 ais.put(8004, -1); 810 ais.put(8005, 10); 811 ais.put(8006, 22); 812 ais.put(8007, -1); 813 ais.put(8008, -1); 814 ais.put(8018, 22); 815 ais.put(8020, -1); 816 ais.put(8100, 10); 817 ais.put(8101, 14); 818 ais.put(8102, 6); 819 for (int k = 90; k < 100; ++k) 820 ais.put(k, -1); 821 } 822}