From aa33b1cd04e69bef1e03568627bd25b114d86203 Mon Sep 17 00:00:00 2001 From: "bas5winkel@gmail.com" Date: Wed, 11 Jan 2012 17:04:22 +0000 Subject: [PATCH] updated all actionscript files in accordance with the core library revision 1901 git-svn-id: https://zxing.googlecode.com/svn/trunk@2111 59b500cc-1b3d-0410-9834-0bbf25fbcc57 --- .../zxing/pdf417/encoder/BarcodeMatrix.as | 96 +++ .../google/zxing/pdf417/encoder/BarcodeRow.as | 93 +++ .../com/google/zxing/pdf417/encoder/PDF417.as | 757 ++++++++++++++++++ .../pdf417/encoder/PDF417ErrorCorrection.as | 211 +++++ .../pdf417/encoder/PDF417HighLevelEncoder.as | 591 ++++++++++++++ .../zxing/pdf417/encoder/PDF417Writer.as | 123 +++ 6 files changed, 1871 insertions(+) create mode 100644 actionscript/core/src/com/google/zxing/pdf417/encoder/BarcodeMatrix.as create mode 100644 actionscript/core/src/com/google/zxing/pdf417/encoder/BarcodeRow.as create mode 100644 actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417.as create mode 100644 actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417ErrorCorrection.as create mode 100644 actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417HighLevelEncoder.as create mode 100644 actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417Writer.as diff --git a/actionscript/core/src/com/google/zxing/pdf417/encoder/BarcodeMatrix.as b/actionscript/core/src/com/google/zxing/pdf417/encoder/BarcodeMatrix.as new file mode 100644 index 000000000..d535d8555 --- /dev/null +++ b/actionscript/core/src/com/google/zxing/pdf417/encoder/BarcodeMatrix.as @@ -0,0 +1,96 @@ +/* + * Copyright 2011 ZXing authors + * + * 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 com.google.zxing.pdf417.encoder +{ + +/** + * Holds all of the information for a barcode in a format where it can be easily accessable + * + * @author Jacob Haynes + */ +public final class BarcodeMatrix { + + private var matrix:Array; + private var currentRow:int; + private var height:int; + private var width:int; + + /** + * @param height the height of the matrix (Rows) + * @param width the width of the matrix (Cols) + */ + public function BarcodeMatrix(height:int,width:int) + { + matrix = new Array(height + 2); + //Initializes the array to the correct width + var matrixLength:int = matrix.length; + var al:int = (width + 4) * 17 + 1; + for (var i:int = 0; i < matrixLength; i++) + { + matrix[i] = new BarcodeRow(al); + + } + this.width = width * 17; + this.height = height + 2; + this.currentRow = 0; + } + + public function set_value(x:int, y:int, value:*):void { + matrix[y].set_value(x, value); + } + + public function setMatrix(x:int, y:int, black:Boolean):void { + set_value(x, y, (black ? 1 : 0)); + } + + public function startRow():void { + ++currentRow; + } + + public function getCurrentRow():BarcodeRow { + return (matrix[currentRow] as BarcodeRow); + } + + public function getMatrix():Array { + return getScaledMatrix(1, 1); + } + + public function getScaledMatrix( xScale:int, yScale:int =-1):Array + { + if (yScale == -1) { yScale = xScale; } + var matrixOut:Array = new Array();//height * yScale)[width * xScale]; + var yMax:int = height * yScale; + for (var ii:int = 0; ii < yMax; ii++) + { + var temp:Array = matrix[int(ii / yScale)].getScaledRow(xScale); + matrixOut[yMax - ii - 1] = temp + } + return matrixOut; + } + + public function toString():String + { + var resultString:String = ""; + for (var i:int=0;i 90) { + throw new WriterException( + "The message doesn't fit in the configured symbol size." + + " The resultant number of rows for this barcode exceeds 90." + + " Please increase the number of columns or decrease the error correction" + + " level to reduce the number of rows."); + } + if (r < 2) { + throw new WriterException( + "The message is too short for the configured symbol size." + + " The resultant number of rows is less than 3." + + " Please decrease the number of columns or increase the error correction" + + " level to increase the number of rows."); + } + return r; + } + + /** + * Calculates the necessary number of rows as described in annex Q of ISO/IEC 15438:2001(E). + * + * @param m the number of source codewords prior to the additional of the Symbol Length + * Descriptor and any pad codewords + * @param k the number of error correction codewords + * @param c the number of columns in the symbol in the data region (excluding start, stop and + * row indicator codewords) + * @return the number of rows in the symbol (r) + */ + private static function calculateNumberOfRows(m:int, k:int, c:int):int { + var r:int = int((m + 1 + k) / c) + 1; + if (c * r >= (m + 1 + k + c)) { + r--; + } + return r; + } + + /** + * Calculates the number of pad codewords as described in 4.9.2 of ISO/IEC 15438:2001(E). + * + * @param m the number of source codewords prior to the additional of the Symbol Length + * Descriptor and any pad codewords + * @param k the number of error correction codewords + * @param c the number of columns in the symbol in the data region (excluding start, stop and + * row indicator codewords) + * @param r the number of rows in the symbol + * @return the number of pad codewords + */ + private static function getNumberOfPadCodewords(m:int, k:int, c:int, r:int):int { + var n:int = c * r - k; + return n > m + 1 ? n - m - 1 : 0; + } + + /** + * Calculates the number of data codewords (equals the Symbol Length Descriptor). + * + * @param m the number of source codewords prior to the additional of the Symbol Length + * Descriptor and any pad codewords + * @param errorCorrectionLevel the error correction level (value between 0 and 8) + * @param c the number of columns in the symbol in the data region (excluding start, stop and + * row indicator codewords) + * @return the number of data codewords + */ + private static function getNumberOfDataCodewords(m:int, errorCorrectionLevel:int, c:int):int { + var k:int = PDF417ErrorCorrection.getErrorCorrectionCodewordCount(errorCorrectionLevel); + var r:int = getNumberOfRows(m, k, c); + return c * r - k; + } + + private static function encodeChar(pattern:int, len:int, logic:BarcodeRow):void { + var map:int = 1 << len - 1; + var last:Boolean = (pattern & map) != 0; //Initialize to inverse of first bit + var width:int = 0; + for (var i:int = 0; i < len; i++) { + var black:Boolean = (pattern & map) != 0; + if (last == black) { + width++; + } else { + logic.addBar(last, width); + + last = black; + width = 1; + } + map >>= 1; + } + logic.addBar(last, width); + } + + private function encodeLowLevel(fullCodewords:String, c:int, r:int, errorCorrectionLevel:int, logic:BarcodeMatrix ):void { + + this.errorCorrectionLevel = errorCorrectionLevel; + + var idx:int = 0; + for (var y:int = 0; y < r; y++) { + var cluster:int = y % 3; + logic.startRow(); + var bcr:BarcodeRow = logic.getCurrentRow(); + encodeChar(START_PATTERN, 17, bcr ); + + var left:int; + var right:int; + if (cluster == 0) { + left = (30 * int(y / 3)) + int((r - 1) / 3); + right = (30 * int(y / 3)) + (c - 1); + } else if (cluster == 1) { + left = (30 * int(y / 3)) + (errorCorrectionLevel * 3) + int((r - 1) % 3); + right = (30 * int(y / 3)) + int((r - 1) / 3); + } else { + left = (30 * int(y / 3)) + (c - 1); + right = (30 * int(y / 3)) + (errorCorrectionLevel * 3) + int((r - 1) % 3); + } + + var pattern:int = CODEWORD_TABLE[cluster][left]; + encodeChar(pattern, 17, logic.getCurrentRow()); + + for (var x:int = 0; x < c; x++) { + pattern = CODEWORD_TABLE[cluster][fullCodewords.charCodeAt(idx)]; + encodeChar(pattern, 17, logic.getCurrentRow()); + idx++; + } + + pattern = CODEWORD_TABLE[cluster][right]; + encodeChar(pattern, 17, logic.getCurrentRow()); + + encodeChar(STOP_PATTERN, 18, logic.getCurrentRow()); + } + } + + /** + * Generates the barcode logic. + * + * @param msg the message to encode + */ + public function generateBarcodeLogic(msg:String, errorCorrectionLevel:int):void { + + //1. step: High-level encoding + var errorCorrectionCodeWords:int = PDF417ErrorCorrection.getErrorCorrectionCodewordCount(errorCorrectionLevel); + + // actionscript does not allow static code parts so we need an instance of the PDF417HighLevelEncoder + var phe:PDF417HighLevelEncoder = new PDF417HighLevelEncoder(); + var highLevel:String = phe.encodeHighLevel(msg); + var sourceCodeWords:int = highLevel.length; + + var dimension:Array = determineDimensions(sourceCodeWords); + + if (dimension == null) { + throw new WriterException("Unable to fit message in columns"); + } + + var cols:int = dimension[0]; + var rows:int = dimension[1]; + + var pad:int = getNumberOfPadCodewords(sourceCodeWords, errorCorrectionCodeWords, cols, rows); + + //2. step: construct data codewords + var n:int = getNumberOfDataCodewords(sourceCodeWords, errorCorrectionLevel, cols); + if (n > 929) { + throw new WriterException( + "Encoded message contains to many code words, message to big (" + msg.length + " bytes)"); + } + + var sb:StringBuilder = new StringBuilder(n); + sb.Append(String.fromCharCode(n)); + sb.Append(highLevel); + for (var i:int = 0; i < pad; i++) { + sb.Append("900"); //PAD characters + } + var dataCodewords:String = sb.toString(); + + + //3. step: Error correction + var ec:String = PDF417ErrorCorrection.generateErrorCorrection(dataCodewords, errorCorrectionLevel); + var fullCodewords:String = dataCodewords + ec; + + //4. step: low-level encoding + barcodeMatrix = new BarcodeMatrix(rows, cols); + encodeLowLevel(fullCodewords, cols, rows, errorCorrectionLevel, barcodeMatrix); + + } + + /** + * Determine optimal nr of columns and rows for the specified number of + * codewords. + * + * @param sourceCodeWords number of code words + * @return dimension object containing cols as width and rows as height + */ + public function determineDimensions(sourceCodeWords:int):Array { + + var ratio:Number = 0.0; + var dimension:Array = null; + var errorCorrectionCodeWords:int = PDF417ErrorCorrection.getErrorCorrectionCodewordCount(errorCorrectionLevel); + + for (var cols:int = MIN_COLS; cols <= MAX_COLS; cols++) { + + var rows:int = calculateNumberOfRows(sourceCodeWords, errorCorrectionCodeWords, cols); + + if (rows < MIN_ROWS) { + break; + } + + if (rows > MAX_ROWS) { + continue; + } + + var newRatio:Number = ((17 * cols + 69) * DEFAULT_MODULE_WIDTH) / (rows * HEIGHT); + + // ignore if previous ratio is closer to preferred ratio + var preferredRatio:Number = 3.0; + if (dimension != null && Math.abs(newRatio - preferredRatio) > Math.abs(ratio - preferredRatio)) { + continue; + } + + ratio = newRatio; + dimension = [cols, rows]; + } + + return dimension; + } + + +} +} + diff --git a/actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417ErrorCorrection.as b/actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417ErrorCorrection.as new file mode 100644 index 000000000..3865a2262 --- /dev/null +++ b/actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417ErrorCorrection.as @@ -0,0 +1,211 @@ +/* + * Copyright 2006 Jeremias Maerki in part, and ZXing Authors in part + * + * 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. + */ + +/* + * This file has been modified from its original form in Barcode4J. + */ + +package com.google.zxing.pdf417.encoder +{ + +import com.google.zxing.WriterException; +import com.google.zxing.common.flexdatatypes.IllegalArgumentException; +import com.google.zxing.common.flexdatatypes.StringBuilder; + +/** + * PDF417 error correction code following the algorithm described in ISO/IEC 15438:2001(E) in + * chapter 4.10. + */ +public class PDF417ErrorCorrection { + + /** + * Tables of coefficients for calculating error correction words + * (see annex F, ISO/IEC 15438:2001(E)) + */ + private static var EC_COEFFICIENTS:Array = [ + [27, 917], + [522, 568, 723, 809], + [237, 308, 436, 284, 646, 653, 428, 379], + [274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, + 42, 176, 65], + [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], + [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], + [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], + [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], + [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]]; + + public function PDF417ErrorCorrection():void { + } + + /** + * Determines the number of error correction codewords for a specified error correction + * level. + * + * @param errorCorrectionLevel the error correction level (0-8) + * @return the number of codewords generated for error correction + */ + public static function getErrorCorrectionCodewordCount(errorCorrectionLevel:int):int { + if (errorCorrectionLevel < 0 || errorCorrectionLevel > 8) { + throw new IllegalArgumentException("Error correction level must be between 0 and 8!"); + } + return 1 << (errorCorrectionLevel + 1); + } + + /** + * Returns the recommended minimum error correction level as described in annex E of + * ISO/IEC 15438:2001(E). + * + * @param n the number of data codewords + * @return the recommended minimum error correction level + */ + public static function getRecommendedMinimumErrorCorrectionLevel(n:int):int { + if (n <= 0) { + throw new IllegalArgumentException("n must be > 0"); + } + if (n <= 40) { + return 2; + } + if (n <= 160) { + return 3; + } + if (n <= 320) { + return 4; + } + if (n <= 863) { + return 5; + } + throw new WriterException("No recommendation possible"); + } + + /** + * Generates the error correction codewords according to 4.10 in ISO/IEC 15438:2001(E). + * + * @param dataCodewords the data codewords + * @param errorCorrectionLevel the error correction level (0-8) + * @return the String representing the error correction codewords + */ + public static function generateErrorCorrection(dataCodewords:String, errorCorrectionLevel:int):String { + var k:int = getErrorCorrectionCodewordCount(errorCorrectionLevel); + var e:Array = new Array(k);for(var i1:int=0;i1= 1; j--) + { + t2 = (t1 * EC_COEFFICIENTS[errorCorrectionLevel][j]) % 929; + t3 = 929 - t2; + e[j] = ((e[j - 1] + t3) % 929); + } + t2 = (t1 * EC_COEFFICIENTS[errorCorrectionLevel][0]) % 929; + t3 = 929 - t2; + e[0] = (t3 % 929); + + } + var sb:StringBuilder = new StringBuilder(k); + for (var j:int = k - 1; j >= 0; j--) { + if (e[j] != 0) + { + e[j] = 929 - (e[j]); + } + sb.Append(String.fromCharCode(e[j])); + } + return sb.ToString(); + } +} +} diff --git a/actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417HighLevelEncoder.as b/actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417HighLevelEncoder.as new file mode 100644 index 000000000..6f2ba3884 --- /dev/null +++ b/actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417HighLevelEncoder.as @@ -0,0 +1,591 @@ +/* + * Copyright 2006 Jeremias Maerki in part, and ZXing Authors in part + * + * 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. + */ + +/* + * This file has been modified from its original form in Barcode4J. + */ + +package com.google.zxing.pdf417.encoder +{ + +import com.google.zxing.WriterException; +import com.google.zxing.common.flexdatatypes.StringBuilder; + +/** + * PDF417 high-level encoder following the algorithm described in ISO/IEC 15438:2001(E) in + * annex P. + */ +public class PDF417HighLevelEncoder +{ + + /** + * code for Text compaction + */ + private static var TEXT_COMPACTION:int = 0; + + /** + * code for Byte compaction + */ + private static var BYTE_COMPACTION:int = 1; + + /** + * code for Numeric compaction + */ + private static var NUMERIC_COMPACTION:int = 2; + + /** + * Text compaction submode Alpha + */ + private static var SUBMODE_ALPHA:int = 0; + + /** + * Text compaction submode Lower + */ + private static var SUBMODE_LOWER:int = 1; + + /** + * Text compaction submode Mixed + */ + private static var SUBMODE_MIXED:int = 2; + + /** + * Text compaction submode Punctuation + */ + private static var SUBMODE_PUNCTUATION:int = 3; + + /** + * mode latch to Text Compaction mode + */ + private static var LATCH_TO_TEXT:int = 900; + + /** + * mode latch to Byte Compaction mode (number of characters NOT a multiple of 6) + */ + private static var LATCH_TO_BYTE_PADDED:int = 901; + + /** + * mode latch to Numeric Compaction mode + */ + private static var LATCH_TO_NUMERIC:int = 902; + + /** + * mode shift to Byte Compaction mode + */ + private static var SHIFT_TO_BYTE:int = 913; + + /** + * mode latch to Byte Compaction mode (number of characters a multiple of 6) + */ + private static var LATCH_TO_BYTE:int = 924; + + /** + * Raw code table for text compaction Mixed sub-mode + */ + private static var TEXT_MIXED_RAW:Array = [ + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 38, 13, 9, 44, 58, + 35, 45, 46, 36, 47, 43, 37, 42, 61, 94, 0, 32, 0, 0, 0]; + + /** + * Raw code table for text compaction: Punctuation sub-mode + */ + private static var TEXT_PUNCTUATION_RAW:Array = [ + 59, 60, 62, 64, 91, 92, 93, 95, 96, 126, 33, 13, 9, 44, 58, + 10, 45, 46, 36, 47, 34, 124, 42, 40, 41, 63, 123, 125, 39, 0]; + + private static var MIXED:Array = new Array(128); + private static var PUNCTUATION:Array = new Array(128); + + public function PDF417HighLevelEncoder():void + { + + for (var i:int = 0; i < MIXED.length; i++) { + MIXED[i] = -1; + } + for (i = 0; i < TEXT_MIXED_RAW.length; i++) { + var b:int = TEXT_MIXED_RAW[i]; + if (b > 0) { + MIXED[b] = i; + } + } + for (i = 0; i < PUNCTUATION.length; i++) { + PUNCTUATION[i] = -1; + } + for (i = 0; i < TEXT_PUNCTUATION_RAW.length; i++) { + b = TEXT_PUNCTUATION_RAW[i]; + if (b > 0) { + PUNCTUATION[b] = i; + } + } + } + + /** + * Converts the message to a byte array using the default encoding (cp437) as defined by the + * specification + * + * @param msg the message + * @return the byte array of the message + */ + private static function getBytesForMessage(msg:String):Array { + return msg.split(""); + } + + /** + * Performs high-level encoding of a PDF417 message using the algorithm described in annex P + * of ISO/IEC 15438:2001(E). + * + * @param msg the message + * @return the encoded message (the char values range from 0 to 928) + */ + public function encodeHighLevel(msg:String):String { + var bytes:Array = null; //Fill later and only if needed + + //the codewords 0..928 are encoded as Unicode characters + var sb:StringBuilder = new StringBuilder(msg.length); + + var len:int = msg.length; + var p:int = 0; + var encodingMode:int = TEXT_COMPACTION; //Default mode, see 4.4.2.1 + var textSubMode:int = SUBMODE_ALPHA; + while (p < len) { + var n:int = determineConsecutiveDigitCount(msg, p); + if (n >= 13) { + sb.Append(String.fromCharCode(LATCH_TO_NUMERIC)); + encodingMode = NUMERIC_COMPACTION; + textSubMode = SUBMODE_ALPHA; //Reset after latch + encodeNumeric(msg, p, n, sb); + p += n; + } else { + var t:int = determineConsecutiveTextCount(msg, p); + if (t >= 5 || n == len) { + if (encodingMode != TEXT_COMPACTION) { + sb.Append(String.fromCharCode(LATCH_TO_TEXT)); + encodingMode = TEXT_COMPACTION; + textSubMode = SUBMODE_ALPHA; //start with submode alpha after latch + } + textSubMode = encodeText(msg, p, t, sb, textSubMode); + p += t; + } else { + if (bytes == null) { + bytes = getBytesForMessage(msg); + } + var b:int = determineConsecutiveBinaryCount(msg, bytes, p); + if (b == 0) { + b = 1; + } + if (b == 1 && encodingMode == TEXT_COMPACTION) { + //Switch for one byte (instead of latch) + encodeBinary(bytes, p, 1, TEXT_COMPACTION, sb); + } else { + //Mode latch performed by encodeBinary() + encodeBinary(bytes, p, b, encodingMode, sb); + encodingMode = BYTE_COMPACTION; + textSubMode = SUBMODE_ALPHA; //Reset after latch + } + p += b; + } + } + } + + return sb.toString(); + } + /* + public static function toHexString(_string:String):String + + { + var r=""; + var e=_string.length; + var c=0; + var h; + while(c= count) { + break; + } + } + var h:String = ""; + var len:int = tmp.length; + + + for (var i:int = 0; i < len; i++) { + var odd:Boolean = (i % 2) != 0; + if (odd) { + + h = String.fromCharCode((h.charCodeAt(0) * 30) + (tmp.charAt(i)).charCodeAt(0)); + var code:int = h.charCodeAt(0); + sb.Append(h); + } else { + h = tmp.charAt(i); + } + } + if ((len % 2) != 0) { + sb.Append(String.fromCharCode(((h.charCodeAt(0) * 30) + 29))); //ps + } + + return submode; + } + + /** + * Encode parts of the message using Byte Compaction as described in ISO/IEC 15438:2001(E), + * chapter 4.4.3. The Unicode characters will be converted to binary using the cp437 + * codepage. + * + * @param bytes the message converted to a byte array + * @param startpos the start position within the message + * @param count the number of bytes to encode + * @param startmode the mode from which this method starts + * @param sb receives the encoded codewords + */ + private function encodeBinary(bytes:Array, startpos:int , count:int , startmode:int, sb:StringBuilder):void { + if (count == 1 && startmode == TEXT_COMPACTION) { + sb.Append(String.fromCharCode(SHIFT_TO_BYTE)); + } else { + var sixpack:Boolean = (count % 6) == 0; + if (sixpack) { + sb.Append(String.fromCharCode(LATCH_TO_BYTE)); + } else { + sb.Append(String.fromCharCode(LATCH_TO_BYTE_PADDED)); + } + } + + var chars:Array = new Array(5); + var idx:int = startpos; + while ((startpos + count - idx) >= 6) { + var t:Number = 0; + for (i = 0; i < 6; i++) { + t <<= 8; + t += bytes[idx + i] & 0xff; + } + for (i = 0; i < 5; i++) { + chars[i] = (t % 900).toString(); + t /= 900; + } + for (i = chars.length - 1; i >= 0; i--) { + sb.Append(chars[i]); + } + idx += 6; + } + //Encode rest (remaining n<5 bytes if any) + for (var i:int = idx; i < startpos + count; i++) { + var ch:int = bytes[i] & 0xff; + sb.Append(String.fromCharCode(ch)); + } + } + + /* + private static void encodeNumeric(String msg, int startpos, int count, StringBuffer sb) { + int idx = 0; + StringBuffer tmp = new StringBuffer(count / 3 + 1); + BigInteger num900 = BigInteger.valueOf(900); + BigInteger num0 = BigInteger.valueOf(0); + while (idx < count - 1) { + tmp.setLength(0); + int len = Math.min(44, count - idx); + String part = '1' + msg.substring(startpos + idx, startpos + idx + len); + BigInteger bigint = new BigInteger(part); + do { + BigInteger c = bigint.mod(num900); + tmp.append((char) c.intValue()); + bigint = bigint.divide(num900); + } while (!bigint.equals(num0)); + + //Reverse temporary string + for (int i = tmp.length() - 1; i >= 0; i--) { + sb.append(tmp.charAt(i)); + } + idx += len; + } + } + */ + + // TODO either this needs to reimplement BigInteger's functionality to properly handle very + // large numeric strings, even in Java ME, or, we give up Java ME and use the version above + // with BigInteger + + private function encodeNumeric(msg:String, startpos:int, count:int, sb:StringBuilder ):void { + var idx:int = 0; + var tmp:StringBuilder = new StringBuilder(count / 3 + 1); + while (idx < count - 1) { + tmp.setLength(0); + var len:int = Math.min(44, count - idx); + var part:String = '1' + msg.substring(startpos + idx, startpos + idx + len); + var bigint:Number = parseFloat(part); + do { + var c:Number = Math.round((bigint % 900)+0.5); // cast to long + tmp.Append(String.fromCharCode(c)); + bigint = Math.round((bigint / 900)+0.5); // bigint /= 900 + } while (bigint != 0); + + //Reverse temporary string + for (var i:int = tmp.length - 1; i >= 0; i--) { + sb.Append(tmp.charAt(i)); + } + idx += len; + } + } + + private function isDigit(ch:String):Boolean { + return ch.charCodeAt(0) >= 48 && ch.charCodeAt(0) <= 57; + } + + private function isAlphaUpper(ch:String):Boolean { + return ch == ' ' || (ch.charCodeAt(0) >= 65 && ch.charCodeAt(0) <= 90); + } + + private function isAlphaLower(ch:String):Boolean { + return ch == ' ' || (ch.charCodeAt(0) >= 97 && ch.charCodeAt(0) <= 122); + } + + private function isMixed(ch:String):Boolean { + return MIXED[ch.charCodeAt(0)] != -1; + } + + private function isPunctuation(ch:String):Boolean { + return PUNCTUATION[ch.charCodeAt(0)] != -1; + } + + private function isText(ch:String):Boolean { + return ch == "\t" || ch == "\n" || ch == "\r" || (ch.charCodeAt(0) >= 32 && ch.charCodeAt(0) <= 126); + } + + /** + * Determines the number of consecutive characters that are encodable using numeric compaction. + * + * @param msg the message + * @param startpos the start position within the message + * @return the requested character count + */ + private function determineConsecutiveDigitCount(msg:String, startpos:int):int { + var count:int = 0; + var len:int = msg.length; + var idx:int = startpos; + if (idx < len) { + var ch:String = msg.charAt(idx); + while (isDigit(ch) && idx < len) { + count++; + idx++; + if (idx < len) { + ch = msg.charAt(idx); + } + } + } + return count; + } + + /** + * Determines the number of consecutive characters that are encodable using text compaction. + * + * @param msg the message + * @param startpos the start position within the message + * @return the requested character count + */ + private function determineConsecutiveTextCount(msg:String, startpos:int):int { + var len:int = msg.length; + var idx:int = startpos; + while (idx < len) { + var ch:String = msg.charAt(idx); + var numericCount:int = 0; + while (numericCount < 13 && isDigit(ch) && idx < len) { + numericCount++; + idx++; + if (idx < len) { + ch = msg.charAt(idx); + } + } + if (numericCount >= 13) { + return idx - startpos - numericCount; + } + if (numericCount > 0) { + //Heuristic: All text-encodable chars or digits are binary encodable + continue; + } + ch = msg.charAt(idx); + + //Check if character is encodable + if (!isText(ch)) { + break; + } + idx++; + } + return idx - startpos; + } + + /** + * Determines the number of consecutive characters that are encodable using binary compaction. + * + * @param msg the message + * @param bytes the message converted to a byte array + * @param startpos the start position within the message + * @return the requested character count + */ + private function determineConsecutiveBinaryCount(msg:String, bytes:Array, startpos:int):int { + var len:int = msg.length; + var idx:int = startpos; + while (idx < len) { + var ch:String = msg.charAt(idx); + var numericCount:int = 0; + + while (numericCount < 13 && isDigit(ch)) { + numericCount++; + //textCount++; + var i:int = idx + numericCount; + if (i >= len) { + break; + } + ch = msg.charAt(i); + } + if (numericCount >= 13) { + return idx - startpos; + } + var textCount:int = 0; + while (textCount < 5 && isText(ch)) { + textCount++; + i = idx + textCount; + if (i >= len) { + break; + } + ch = msg.charAt(i); + } + if (textCount >= 5) { + return idx - startpos; + } + ch = msg.charAt(idx); + + //Check if character is encodable + //Sun returns a ASCII 63 (?) for a character that cannot be mapped. Let's hope all + //other VMs do the same + if (bytes[idx] == 63 && ch != '?') { + throw new WriterException("Non-encodable character detected: " + ch + " (Unicode: " + ch.toString() + ')'); + } + idx++; + } + return idx - startpos; + } + + + +} + +} diff --git a/actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417Writer.as b/actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417Writer.as new file mode 100644 index 000000000..80c71b9a5 --- /dev/null +++ b/actionscript/core/src/com/google/zxing/pdf417/encoder/PDF417Writer.as @@ -0,0 +1,123 @@ +/* + * Copyright 2011 ZXing authors + * + * 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 com.google.zxing.pdf417.encoder{ + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.Writer; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.common.flexdatatypes.HashTable; +import com.google.zxing.common.flexdatatypes.IllegalArgumentException; +/** + * @author Jacob Haynes + */ +public class PDF417Writer implements Writer +{ + + public function encode(contents:String, format:BarcodeFormat=null, width:int=0, height:int=0, hints:HashTable=null):Object + { + if (format != BarcodeFormat.PDF417) { + throw new IllegalArgumentException("Can only encode PDF_417, but got " + format); + } + + var encoder:PDF417 = new PDF417(); + + //No error correction at the moment + var errorCorrectionLevel:int = 3; + encoder.generateBarcodeLogic(contents, errorCorrectionLevel); + + // Give it data to be encoded + //encoderExt.setData(content.getBytes()); + // Find the Error correction level automatically + + //encoderExt.encode(); + //encoderExt.createArray(); + var lineThickness:int = 3; + var aspectRatio:int = 8; + var originalScale:Array = encoder.getBarcodeMatrix().getScaledMatrix(lineThickness, aspectRatio * lineThickness); + + var rotated:Boolean = false; + var val1:Boolean = (height > width)?true:false; + var val2:Boolean = (originalScale[0].length < originalScale.length)?true:false; + if ((val1 && !val2) || (!val1 && val2)) + { + originalScale = rotateArray(originalScale); + rotated = true; + } + + var scaleX:int = width / originalScale[0].length; + var scaleY:int = height / originalScale.length; + + var scale:int; + if (scaleX < scaleY) { + scale = scaleX; + } else { + scale = scaleY; + } + + if (scale > 1) { + var scaledMatrix:Array = + encoder.getBarcodeMatrix().getScaledMatrix(scale * lineThickness, scale * aspectRatio * lineThickness); + if (rotated) { + scaledMatrix = rotateArray(scaledMatrix); + } + return bitMatrixFrombitArray(scaledMatrix); + } + return bitMatrixFrombitArray(originalScale); + } + + /** + * This takes an array holding the values of the PDF 417 + * + * @param input a byte array of information with 0 is black, and 1 is white + * @return BitMatrix of the input + */ + private static function bitMatrixFrombitArray(input:Array):BitMatrix { + //Creates a small whitespace boarder around the barcode + var whiteSpace:int = 30; + + //Creates the bitmatrix with extra space for whitespace + var output:BitMatrix = new BitMatrix(input.length + 2 * whiteSpace, input[0].length + 2 * whiteSpace); + output.clear(); + for (var ii:int = 0; ii < input.length; ii++) { + for (var jj:int = 0; jj < input[0].length; jj++) { + // Zero is white in the bytematrix + if (input[ii][jj] == 1) { + output._set(ii + whiteSpace, jj + whiteSpace); + } + } + } + var result:String = output.toString2(); + return output; + } + + /** + * Takes and rotates the it 90 degrees + */ + private static function rotateArray(bitarray:Array):Array { + var temp:Array = new Array();//byte[bitarray[0].length][bitarray.length]; + for (var ii:int = 0; ii < bitarray.length; ii++) { + // This makes the direction consistent on screen when rotating the + // screen; + var inverseii:int = bitarray.length - ii - 1; + for (var jj:int = 0; jj < bitarray[0].length; jj++) { + temp[jj][inverseii] = bitarray[ii][jj]; + } + } + return temp; + } +} +}