Version 6487

This commit is contained in:
Arnaud Roques 2011-04-19 18:50:40 +02:00
parent 7347fc27af
commit fe6831d641
269 changed files with 26508 additions and 1475 deletions

View File

@ -0,0 +1,103 @@
/*
* Copyright 2007 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;
import java.util.Hashtable;
/**
* Enumerates barcode formats known to this package.
*
* @author Sean Owen
*/
public final class BarcodeFormat {
// No, we can't use an enum here. J2ME doesn't support it.
private static final Hashtable VALUES = new Hashtable();
/** QR Code 2D barcode format. */
public static final BarcodeFormat QR_CODE = new BarcodeFormat("QR_CODE");
/** Data Matrix 2D barcode format. */
public static final BarcodeFormat DATA_MATRIX = new BarcodeFormat("DATA_MATRIX");
/** UPC-E 1D format. */
public static final BarcodeFormat UPC_E = new BarcodeFormat("UPC_E");
/** UPC-A 1D format. */
public static final BarcodeFormat UPC_A = new BarcodeFormat("UPC_A");
/** EAN-8 1D format. */
public static final BarcodeFormat EAN_8 = new BarcodeFormat("EAN_8");
/** EAN-13 1D format. */
public static final BarcodeFormat EAN_13 = new BarcodeFormat("EAN_13");
/** UPC/EAN extension format. Not a stand-alone format. */
public static final BarcodeFormat UPC_EAN_EXTENSION = new BarcodeFormat("UPC_EAN_EXTENSION");
/** Code 128 1D format. */
public static final BarcodeFormat CODE_128 = new BarcodeFormat("CODE_128");
/** Code 39 1D format. */
public static final BarcodeFormat CODE_39 = new BarcodeFormat("CODE_39");
/** Code 93 1D format. */
public static final BarcodeFormat CODE_93 = new BarcodeFormat("CODE_93");
/** CODABAR 1D format. */
public static final BarcodeFormat CODABAR = new BarcodeFormat("CODABAR");
/** ITF (Interleaved Two of Five) 1D format. */
public static final BarcodeFormat ITF = new BarcodeFormat("ITF");
/** RSS 14 */
public static final BarcodeFormat RSS14 = new BarcodeFormat("RSS14");
/** PDF417 format. */
public static final BarcodeFormat PDF417 = new BarcodeFormat("PDF417");
/** RSS EXPANDED */
public static final BarcodeFormat RSS_EXPANDED = new BarcodeFormat("RSS_EXPANDED");
private final String name;
private BarcodeFormat(String name) {
this.name = name;
VALUES.put(name, this);
}
public String getName() {
return name;
}
public String toString() {
return name;
}
public static BarcodeFormat valueOf(String name) {
if (name == null || name.length() == 0) {
throw new IllegalArgumentException();
}
BarcodeFormat format = (BarcodeFormat) VALUES.get(name);
if (format == null) {
throw new IllegalArgumentException();
}
return format;
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2007 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;
/**
* Thrown when a barcode was successfully detected and decoded, but
* was not returned because its checksum feature failed.
*
* @author Sean Owen
*/
public final class ChecksumException extends ReaderException {
private static final ChecksumException instance = new ChecksumException();
private ChecksumException() {
// do nothing
}
public static ChecksumException getChecksumInstance() {
return instance;
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2007 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;
import java.io.Reader;
/**
* Encapsulates a type of hint that a caller may pass to a barcode reader to help it
* more quickly or accurately decode it. It is up to implementations to decide what,
* if anything, to do with the information that is supplied.
*
* @author Sean Owen
* @author dswitkin@google.com (Daniel Switkin)
* @see Reader#decode(BinaryBitmap,java.util.Hashtable)
*/
public final class DecodeHintType {
// No, we can't use an enum here. J2ME doesn't support it.
/**
* Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
*/
public static final DecodeHintType OTHER = new DecodeHintType();
/**
* Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
* use {@link Boolean#TRUE}.
*/
public static final DecodeHintType PURE_BARCODE = new DecodeHintType();
/**
* Image is known to be of one of a few possible formats.
* Maps to a {@link java.util.Vector} of {@link BarcodeFormat}s.
*/
public static final DecodeHintType POSSIBLE_FORMATS = new DecodeHintType();
/**
* Spend more time to try to find a barcode; optimize for accuracy, not speed.
* Doesn't matter what it maps to; use {@link Boolean#TRUE}.
*/
public static final DecodeHintType TRY_HARDER = new DecodeHintType();
/**
* Specifies what character encoding to use when decoding, where applicable (type String)
*/
public static final DecodeHintType CHARACTER_SET = new DecodeHintType();
/**
* Allowed lengths of encoded data -- reject anything else. Maps to an int[].
*/
public static final DecodeHintType ALLOWED_LENGTHS = new DecodeHintType();
/**
* Assume Code 39 codes employ a check digit. Maps to {@link Boolean}.
*/
public static final DecodeHintType ASSUME_CODE_39_CHECK_DIGIT = new DecodeHintType();
/**
* The caller needs to be notified via callback when a possible {@link ResultPoint}
* is found. Maps to a {@link ResultPointCallback}.
*/
public static final DecodeHintType NEED_RESULT_POINT_CALLBACK = new DecodeHintType();
private DecodeHintType() {
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2008 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;
/**
* These are a set of hints that you may pass to Writers to specify their behavior.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class EncodeHintType {
/**
* Specifies what degree of error correction to use, for example in QR Codes (type Integer).
*/
public static final EncodeHintType ERROR_CORRECTION = new EncodeHintType();
/**
* Specifies what character encoding to use where applicable (type String)
*/
public static final EncodeHintType CHARACTER_SET = new EncodeHintType();
private EncodeHintType() {
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2007 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;
/**
* Thrown when a barcode was successfully detected, but some aspect of
* the content did not conform to the barcode's format rules. This could have
* been due to a mis-detection.
*
* @author Sean Owen
*/
public final class FormatException extends ReaderException {
private static final FormatException instance = new FormatException();
private FormatException() {
// do nothing
}
public static FormatException getFormatInstance() {
return instance;
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2007 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;
/**
* Thrown when a barcode was not found in the image. It might have been
* partially detected but could not be confirmed.
*
* @author Sean Owen
*/
public final class NotFoundException extends ReaderException {
private static final NotFoundException instance = new NotFoundException();
private NotFoundException() {
// do nothing
}
public static NotFoundException getNotFoundInstance() {
return instance;
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright 2007 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;
/**
* The general exception class throw when something goes wrong during decoding of a barcode.
* This includes, but is not limited to, failing checksums / error correction algorithms, being
* unable to locate finder timing patterns, and so on.
*
* @author Sean Owen
*/
public abstract class ReaderException extends Exception {
// TODO: Currently we throw up to 400 ReaderExceptions while scanning a single 240x240 image before
// rejecting it. This involves a lot of overhead and memory allocation, and affects both performance
// and latency on continuous scan clients. In the future, we should change all the decoders not to
// throw exceptions for routine events, like not finding a barcode on a given row. Instead, we
// should return error codes back to the callers, and simply delete this class. In the mean time, I
// have altered this class to be as lightweight as possible, by ignoring the exception string, and
// by disabling the generation of stack traces, which is especially time consuming. These are just
// temporary measures, pending the big cleanup.
//private static final ReaderException instance = new ReaderException();
// EXCEPTION TRACKING SUPPORT
// Identifies who is throwing exceptions and how often. To use:
//
// 1. Uncomment these lines and the code below which uses them.
// 2. Uncomment the two corresponding lines in j2se/CommandLineRunner.decode()
// 3. Change core to build as Java 1.5 temporarily
// private static int exceptionCount = 0;
// private static Map<String,Integer> throwers = new HashMap<String,Integer>(32);
ReaderException() {
// do nothing
}
//public static ReaderException getInstance() {
// Exception e = new Exception();
// // Take the stack frame before this one.
// StackTraceElement stack = e.getStackTrace()[1];
// String key = stack.getClassName() + "." + stack.getMethodName() + "(), line " +
// stack.getLineNumber();
// if (throwers.containsKey(key)) {
// Integer value = throwers.get(key);
// value++;
// throwers.put(key, value);
// } else {
// throwers.put(key, 1);
// }
// exceptionCount++;
//return instance;
//}
// public static int getExceptionCountAndReset() {
// int temp = exceptionCount;
// exceptionCount = 0;
// return temp;
// }
//
// public static String getThrowersAndReset() {
// StringBuilder builder = new StringBuilder(1024);
// Object[] keys = throwers.keySet().toArray();
// for (int x = 0; x < keys.length; x++) {
// String key = (String) keys[x];
// Integer value = throwers.get(key);
// builder.append(key);
// builder.append(": ");
// builder.append(value);
// builder.append("\n");
// }
// throwers.clear();
// return builder.toString();
// }
// Prevent stack traces from being taken
// srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden?
// This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
public final Throwable fillInStackTrace() {
return null;
}
}

View File

@ -0,0 +1,127 @@
/*
* Copyright 2007 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;
/**
* <p>Encapsulates a point of interest in an image containing a barcode. Typically, this
* would be the location of a finder pattern or the corner of the barcode, for example.</p>
*
* @author Sean Owen
*/
public class ResultPoint {
private final float x;
private final float y;
public ResultPoint(float x, float y) {
this.x = x;
this.y = y;
}
public final float getX() {
return x;
}
public final float getY() {
return y;
}
public boolean equals(Object other) {
if (other instanceof ResultPoint) {
ResultPoint otherPoint = (ResultPoint) other;
return x == otherPoint.x && y == otherPoint.y;
}
return false;
}
public int hashCode() {
return 31 * Float.floatToIntBits(x) + Float.floatToIntBits(y);
}
public String toString() {
StringBuffer result = new StringBuffer(25);
result.append('(');
result.append(x);
result.append(',');
result.append(y);
result.append(')');
return result.toString();
}
/**
* <p>Orders an array of three ResultPoints in an order [A,B,C] such that AB < AC and
* BC < AC and the angle between BC and BA is less than 180 degrees.
*/
public static void orderBestPatterns(ResultPoint[] patterns) {
// Find distances between pattern centers
float zeroOneDistance = distance(patterns[0], patterns[1]);
float oneTwoDistance = distance(patterns[1], patterns[2]);
float zeroTwoDistance = distance(patterns[0], patterns[2]);
ResultPoint pointA, pointB, pointC;
// Assume one closest to other two is B; A and C will just be guesses at first
if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) {
pointB = patterns[0];
pointA = patterns[1];
pointC = patterns[2];
} else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) {
pointB = patterns[1];
pointA = patterns[0];
pointC = patterns[2];
} else {
pointB = patterns[2];
pointA = patterns[0];
pointC = patterns[1];
}
// Use cross product to figure out whether A and C are correct or flipped.
// This asks whether BC x BA has a positive z component, which is the arrangement
// we want for A, B, C. If it's negative, then we've got it flipped around and
// should swap A and C.
if (crossProductZ(pointA, pointB, pointC) < 0.0f) {
ResultPoint temp = pointA;
pointA = pointC;
pointC = temp;
}
patterns[0] = pointA;
patterns[1] = pointB;
patterns[2] = pointC;
}
/**
* @return distance between two points
*/
public static float distance(ResultPoint pattern1, ResultPoint pattern2) {
float xDiff = pattern1.getX() - pattern2.getX();
float yDiff = pattern1.getY() - pattern2.getY();
return (float) Math.sqrt((double) (xDiff * xDiff + yDiff * yDiff));
}
/**
* Returns the z component of the cross product between vectors BC and BA.
*/
private static float crossProductZ(ResultPoint pointA, ResultPoint pointB, ResultPoint pointC) {
float bX = pointB.x;
float bY = pointB.y;
return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2008 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;
import java.util.Hashtable;
import com.google.zxing.common.BitMatrix;
/**
* The base class for all objects which encode/generate a barcode image.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public interface Writer {
/**
* Encode a barcode using the default settings.
*
* @param contents The contents to encode in the barcode
* @param format The barcode format to generate
* @param width The preferred width in pixels
* @param height The preferred height in pixels
* @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
*/
BitMatrix encode(String contents, BarcodeFormat format, int multiple)
throws WriterException;
/**
*
* @param contents The contents to encode in the barcode
* @param format The barcode format to generate
* @param width The preferred width in pixels
* @param height The preferred height in pixels
* @param hints Additional parameters to supply to the encoder
* @return The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
*/
BitMatrix encode(String contents, BarcodeFormat format, int multiple, Hashtable hints)
throws WriterException;
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2008 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;
/**
* A base class which covers the range of exceptions which may occur when encoding a barcode using
* the Writer framework.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class WriterException extends Exception {
public WriterException() {
super();
}
public WriterException(String message) {
super(message);
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2009 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.client.j2se;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import com.google.zxing.common.BitMatrix;
/**
* Writes a {@link BitMatrix} to {@link BufferedImage},
* file or stream. Provided here instead of core since it depends on
* Java SE libraries.
*
* @author Sean Owen
*/
public final class MatrixToImageWriter {
private static final int BLACK = 0xFF000000;
private static final int WHITE = 0xFFFFFFFF;
private MatrixToImageWriter() {}
/**
* Renders a {@link BitMatrix} as an image, where "false" bits are rendered
* as white, and "true" bits are rendered as black.
*/
public static BufferedImage toBufferedImage(BitMatrix matrix) {
int width = matrix.getWidth();
int height = matrix.getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
}
}
return image;
}
/**
* Writes a {@link BitMatrix} to a file.
*
* @see #toBufferedImage(BitMatrix)
*/
public static void writeToFile(BitMatrix matrix, String format, File file)
throws IOException {
BufferedImage image = toBufferedImage(matrix);
ImageIO.write(image, format, file);
}
/**
* Writes a {@link BitMatrix} to a stream.
*
* @see #toBufferedImage(BitMatrix)
*/
public static void writeToStream(BitMatrix matrix, String format, OutputStream stream)
throws IOException {
BufferedImage image = toBufferedImage(matrix);
ImageIO.write(image, format, stream);
}
}

View File

@ -0,0 +1,247 @@
/*
* Copyright 2007 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.common;
/**
* <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p>
*
* @author Sean Owen
*/
public final class BitArray {
// TODO: I have changed these members to be public so ProGuard can inline get() and set(). Ideally
// they'd be private and we'd use the -allowaccessmodification flag, but Dalvik rejects the
// resulting binary at runtime on Android. If we find a solution to this, these should be changed
// back to private.
public int[] bits;
public int size;
public BitArray() {
this.size = 0;
this.bits = new int[1];
}
public BitArray(int size) {
this.size = size;
this.bits = makeArray(size);
}
public int getSize() {
return size;
}
public int getSizeInBytes() {
return (size + 7) >> 3;
}
private void ensureCapacity(int size) {
if (size > bits.length << 5) {
int[] newBits = makeArray(size);
System.arraycopy(bits, 0, newBits, 0, bits.length);
this.bits = newBits;
}
}
/**
* @param i bit to get
* @return true iff bit i is set
*/
public boolean get(int i) {
return (bits[i >> 5] & (1 << (i & 0x1F))) != 0;
}
/**
* Sets bit i.
*
* @param i bit to set
*/
public void set(int i) {
bits[i >> 5] |= 1 << (i & 0x1F);
}
/**
* Flips bit i.
*
* @param i bit to set
*/
public void flip(int i) {
bits[i >> 5] ^= 1 << (i & 0x1F);
}
/**
* Sets a block of 32 bits, starting at bit i.
*
* @param i first bit to set
* @param newBits the new value of the next 32 bits. Note again that the least-significant bit
* corresponds to bit i, the next-least-significant to i+1, and so on.
*/
public void setBulk(int i, int newBits) {
bits[i >> 5] = newBits;
}
/**
* Clears all bits (sets to false).
*/
public void clear() {
int max = bits.length;
for (int i = 0; i < max; i++) {
bits[i] = 0;
}
}
/**
* Efficient method to check if a range of bits is set, or not set.
*
* @param start start of range, inclusive.
* @param end end of range, exclusive
* @param value if true, checks that bits in range are set, otherwise checks that they are not set
* @return true iff all bits are set or not set in range, according to value argument
* @throws IllegalArgumentException if end is less than or equal to start
*/
public boolean isRange(int start, int end, boolean value) {
if (end < start) {
throw new IllegalArgumentException();
}
if (end == start) {
return true; // empty range matches
}
end--; // will be easier to treat this as the last actually set bit -- inclusive
int firstInt = start >> 5;
int lastInt = end >> 5;
for (int i = firstInt; i <= lastInt; i++) {
int firstBit = i > firstInt ? 0 : start & 0x1F;
int lastBit = i < lastInt ? 31 : end & 0x1F;
int mask;
if (firstBit == 0 && lastBit == 31) {
mask = -1;
} else {
mask = 0;
for (int j = firstBit; j <= lastBit; j++) {
mask |= 1 << j;
}
}
// Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
// equals the mask, or we're looking for 0s and the masked portion is not all 0s
if ((bits[i] & mask) != (value ? mask : 0)) {
return false;
}
}
return true;
}
public void appendBit(boolean bit) {
ensureCapacity(size + 1);
if (bit) {
bits[size >> 5] |= (1 << (size & 0x1F));
}
size++;
}
/**
* Appends the least-significant bits, from value, in order from most-significant to
* least-significant. For example, appending 6 bits from 0x000001E will append the bits
* 0, 1, 1, 1, 1, 0 in that order.
*/
public void appendBits(int value, int numBits) {
if (numBits < 0 || numBits > 32) {
throw new IllegalArgumentException("Num bits must be between 0 and 32");
}
ensureCapacity(size + numBits);
for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) {
appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1);
}
}
public void appendBitArray(BitArray other) {
int otherSize = other.getSize();
ensureCapacity(size + otherSize);
for (int i = 0; i < otherSize; i++) {
appendBit(other.get(i));
}
}
public void xor(BitArray other) {
if (bits.length != other.bits.length) {
throw new IllegalArgumentException("Sizes don't match");
}
for (int i = 0; i < bits.length; i++) {
// The last byte could be incomplete (i.e. not have 8 bits in
// it) but there is no problem since 0 XOR 0 == 0.
bits[i] ^= other.bits[i];
}
}
/**
*
* @param bitOffset first bit to start writing
* @param array array to write into. Bytes are written most-significant byte first. This is the opposite
* of the internal representation, which is exposed by {@link #getBitArray()}
* @param offset position in array to start writing
* @param numBytes how many bytes to write
*/
public void toBytes(int bitOffset, byte[] array, int offset, int numBytes) {
for (int i = 0; i < numBytes; i++) {
int theByte = 0;
for (int j = 0; j < 8; j++) {
if (get(bitOffset)) {
theByte |= 1 << (7 - j);
}
bitOffset++;
}
array[offset + i] = (byte) theByte;
}
}
/**
* @return underlying array of ints. The first element holds the first 32 bits, and the least
* significant bit is bit 0.
*/
public int[] getBitArray() {
return bits;
}
/**
* Reverses all bits in the array.
*/
public void reverse() {
int[] newBits = new int[bits.length];
int size = this.size;
for (int i = 0; i < size; i++) {
if (get(size - i - 1)) {
newBits[i >> 5] |= 1 << (i & 0x1F);
}
}
bits = newBits;
}
private static int[] makeArray(int size) {
return new int[(size + 31) >> 5];
}
public String toString() {
StringBuffer result = new StringBuffer(size);
for (int i = 0; i < size; i++) {
if ((i & 0x07) == 0) {
result.append(' ');
}
result.append(get(i) ? 'X' : '.');
}
return result.toString();
}
}

View File

@ -0,0 +1,226 @@
/*
* Copyright 2007 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.common;
/**
* <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common
* module, x is the column position, and y is the row position. The ordering is always x, y.
* The origin is at the top-left.</p>
*
* <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins
* with a new int. This is done intentionally so that we can copy out a row into a BitArray very
* efficiently.</p>
*
* <p>The ordering of bits is row-major. Within each int, the least significant bits are used first,
* meaning they represent lower x values. This is compatible with BitArray's implementation.</p>
*
* @author Sean Owen
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class BitMatrix {
// TODO: Just like BitArray, these need to be public so ProGuard can inline them.
public final int width;
public final int height;
public final int rowSize;
public final int[] bits;
// A helper to construct a square matrix.
public BitMatrix(int dimension) {
this(dimension, dimension);
}
public BitMatrix(int width, int height) {
if (width < 1 || height < 1) {
throw new IllegalArgumentException("Both dimensions must be greater than 0");
}
this.width = width;
this.height = height;
this.rowSize = (width + 31) >> 5;
bits = new int[rowSize * height];
}
/**
* <p>Gets the requested bit, where true means black.</p>
*
* @param x The horizontal component (i.e. which column)
* @param y The vertical component (i.e. which row)
* @return value of given bit in matrix
*/
public boolean get(int x, int y) {
int offset = y * rowSize + (x >> 5);
return ((bits[offset] >>> (x & 0x1f)) & 1) != 0;
}
/**
* <p>Sets the given bit to true.</p>
*
* @param x The horizontal component (i.e. which column)
* @param y The vertical component (i.e. which row)
*/
public void set(int x, int y) {
int offset = y * rowSize + (x >> 5);
bits[offset] |= 1 << (x & 0x1f);
}
/**
* <p>Flips the given bit.</p>
*
* @param x The horizontal component (i.e. which column)
* @param y The vertical component (i.e. which row)
*/
public void flip(int x, int y) {
int offset = y * rowSize + (x >> 5);
bits[offset] ^= 1 << (x & 0x1f);
}
/**
* Clears all bits (sets to false).
*/
public void clear() {
int max = bits.length;
for (int i = 0; i < max; i++) {
bits[i] = 0;
}
}
/**
* <p>Sets a square region of the bit matrix to true.</p>
*
* @param left The horizontal position to begin at (inclusive)
* @param top The vertical position to begin at (inclusive)
* @param width The width of the region
* @param height The height of the region
*/
public void setRegion(int left, int top, int width, int height) {
if (top < 0 || left < 0) {
throw new IllegalArgumentException("Left and top must be nonnegative");
}
if (height < 1 || width < 1) {
throw new IllegalArgumentException("Height and width must be at least 1");
}
int right = left + width;
int bottom = top + height;
if (bottom > this.height || right > this.width) {
throw new IllegalArgumentException("The region must fit inside the matrix");
}
for (int y = top; y < bottom; y++) {
int offset = y * rowSize;
for (int x = left; x < right; x++) {
bits[offset + (x >> 5)] |= 1 << (x & 0x1f);
}
}
}
/**
* A fast method to retrieve one row of data from the matrix as a BitArray.
*
* @param y The row to retrieve
* @param row An optional caller-allocated BitArray, will be allocated if null or too small
* @return The resulting BitArray - this reference should always be used even when passing
* your own row
*/
public BitArray getRow(int y, BitArray row) {
if (row == null || row.getSize() < width) {
row = new BitArray(width);
}
int offset = y * rowSize;
for (int x = 0; x < rowSize; x++) {
row.setBulk(x << 5, bits[offset + x]);
}
return row;
}
/**
* This is useful in detecting a corner of a 'pure' barcode.
*
* @return {x,y} coordinate of top-left-most 1 bit, or null if it is all white
*/
public int[] getTopLeftOnBit() {
int bitsOffset = 0;
while (bitsOffset < bits.length && bits[bitsOffset] == 0) {
bitsOffset++;
}
if (bitsOffset == bits.length) {
return null;
}
int y = bitsOffset / rowSize;
int x = (bitsOffset % rowSize) << 5;
int theBits = bits[bitsOffset];
int bit = 0;
while ((theBits << (31-bit)) == 0) {
bit++;
}
x += bit;
return new int[] {x, y};
}
/**
* @return The width of the matrix
*/
public int getWidth() {
return width;
}
/**
* @return The height of the matrix
*/
public int getHeight() {
return height;
}
public boolean equals(Object o) {
if (!(o instanceof BitMatrix)) {
return false;
}
BitMatrix other = (BitMatrix) o;
if (width != other.width || height != other.height ||
rowSize != other.rowSize || bits.length != other.bits.length) {
return false;
}
for (int i = 0; i < bits.length; i++) {
if (bits[i] != other.bits[i]) {
return false;
}
}
return true;
}
public int hashCode() {
int hash = width;
hash = 31 * hash + width;
hash = 31 * hash + height;
hash = 31 * hash + rowSize;
for (int i = 0; i < bits.length; i++) {
hash = 31 * hash + bits[i];
}
return hash;
}
public String toString() {
StringBuffer result = new StringBuffer(height * (width + 1));
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
result.append(get(x, y) ? "X " : " ");
}
result.append('\n');
}
return result.toString();
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2007 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.common;
/**
* <p>This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
* number of bits read is not often a multiple of 8.</p>
*
* <p>This class is thread-safe but not reentrant. Unless the caller modifies the bytes array
* it passed in, in which case all bets are off.</p>
*
* @author Sean Owen
*/
public final class BitSource {
private final byte[] bytes;
private int byteOffset;
private int bitOffset;
/**
* @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
* Bits are read within a byte from most-significant to least-significant bit.
*/
public BitSource(byte[] bytes) {
this.bytes = bytes;
}
/**
* @param numBits number of bits to read
* @return int representing the bits read. The bits will appear as the least-significant
* bits of the int
* @throws IllegalArgumentException if numBits isn't in [1,32]
*/
public int readBits(int numBits) {
if (numBits < 1 || numBits > 32) {
throw new IllegalArgumentException();
}
int result = 0;
// First, read remainder from current byte
if (bitOffset > 0) {
int bitsLeft = 8 - bitOffset;
int toRead = numBits < bitsLeft ? numBits : bitsLeft;
int bitsToNotRead = bitsLeft - toRead;
int mask = (0xFF >> (8 - toRead)) << bitsToNotRead;
result = (bytes[byteOffset] & mask) >> bitsToNotRead;
numBits -= toRead;
bitOffset += toRead;
if (bitOffset == 8) {
bitOffset = 0;
byteOffset++;
}
}
// Next read whole bytes
if (numBits > 0) {
while (numBits >= 8) {
result = (result << 8) | (bytes[byteOffset] & 0xFF);
byteOffset++;
numBits -= 8;
}
// Finally read a partial byte
if (numBits > 0) {
int bitsToNotRead = 8 - numBits;
int mask = (0xFF >> bitsToNotRead) << bitsToNotRead;
result = (result << numBits) | ((bytes[byteOffset] & mask) >> bitsToNotRead);
bitOffset += numBits;
}
}
return result;
}
/**
* @return number of bits that can be read successfully
*/
public int available() {
return 8 * (bytes.length - byteOffset) - bitOffset;
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright 2008 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.common;
import java.util.Hashtable;
/**
* Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1
* of ISO 18004.
*
* @author Sean Owen
*/
public final class CharacterSetECI extends ECI {
private static Hashtable VALUE_TO_ECI;
private static Hashtable NAME_TO_ECI;
private static void initialize() {
VALUE_TO_ECI = new Hashtable(29);
NAME_TO_ECI = new Hashtable(29);
// TODO figure out if these values are even right!
addCharacterSet(0, "Cp437");
addCharacterSet(1, new String[] {"ISO8859_1", "ISO-8859-1"});
addCharacterSet(2, "Cp437");
addCharacterSet(3, new String[] {"ISO8859_1", "ISO-8859-1"});
addCharacterSet(4, "ISO8859_2");
addCharacterSet(5, "ISO8859_3");
addCharacterSet(6, "ISO8859_4");
addCharacterSet(7, "ISO8859_5");
addCharacterSet(8, "ISO8859_6");
addCharacterSet(9, "ISO8859_7");
addCharacterSet(10, "ISO8859_8");
addCharacterSet(11, "ISO8859_9");
addCharacterSet(12, "ISO8859_10");
addCharacterSet(13, "ISO8859_11");
addCharacterSet(15, "ISO8859_13");
addCharacterSet(16, "ISO8859_14");
addCharacterSet(17, "ISO8859_15");
addCharacterSet(18, "ISO8859_16");
addCharacterSet(20, new String[] {"SJIS", "Shift_JIS"});
}
private final String encodingName;
private CharacterSetECI(int value, String encodingName) {
super(value);
this.encodingName = encodingName;
}
public String getEncodingName() {
return encodingName;
}
private static void addCharacterSet(int value, String encodingName) {
CharacterSetECI eci = new CharacterSetECI(value, encodingName);
VALUE_TO_ECI.put(new Integer(value), eci); // can't use valueOf
NAME_TO_ECI.put(encodingName, eci);
}
private static void addCharacterSet(int value, String[] encodingNames) {
CharacterSetECI eci = new CharacterSetECI(value, encodingNames[0]);
VALUE_TO_ECI.put(new Integer(value), eci); // can't use valueOf
for (int i = 0; i < encodingNames.length; i++) {
NAME_TO_ECI.put(encodingNames[i], eci);
}
}
/**
* @param value character set ECI value
* @return {@link CharacterSetECI} representing ECI of given value, or null if it is legal but
* unsupported
* @throws IllegalArgumentException if ECI value is invalid
*/
public static CharacterSetECI getCharacterSetECIByValue(int value) {
if (VALUE_TO_ECI == null) {
initialize();
}
if (value < 0 || value >= 900) {
throw new IllegalArgumentException("Bad ECI value: " + value);
}
return (CharacterSetECI) VALUE_TO_ECI.get(new Integer(value));
}
/**
* @param name character set ECI encoding name
* @return {@link CharacterSetECI} representing ECI for character encoding, or null if it is legal
* but unsupported
*/
public static CharacterSetECI getCharacterSetECIByName(String name) {
if (NAME_TO_ECI == null) {
initialize();
}
return (CharacterSetECI) NAME_TO_ECI.get(name);
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2007 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.common;
import java.util.Vector;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
/**
* <p>Encapsulates the result of decoding a matrix of bits. This typically
* applies to 2D barcode formats. For now it contains the raw bytes obtained,
* as well as a String interpretation of those bytes, if applicable.</p>
*
* @author Sean Owen
*/
public final class DecoderResult {
private final byte[] rawBytes;
private final String text;
private final Vector byteSegments;
private final ErrorCorrectionLevel ecLevel;
public DecoderResult(byte[] rawBytes, String text, Vector byteSegments, ErrorCorrectionLevel ecLevel) {
if (rawBytes == null && text == null) {
throw new IllegalArgumentException();
}
this.rawBytes = rawBytes;
this.text = text;
this.byteSegments = byteSegments;
this.ecLevel = ecLevel;
}
public byte[] getRawBytes() {
return rawBytes;
}
public String getText() {
return text;
}
public Vector getByteSegments() {
return byteSegments;
}
public ErrorCorrectionLevel getECLevel() {
return ecLevel;
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2007 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.common;
import com.google.zxing.ResultPoint;
/**
* <p>Encapsulates the result of detecting a barcode in an image. This includes the raw
* matrix of black/white pixels corresponding to the barcode, and possibly points of interest
* in the image, like the location of finder patterns or corners of the barcode in the image.</p>
*
* @author Sean Owen
*/
public final class DetectorResult {
private final BitMatrix bits;
private final ResultPoint[] points;
public DetectorResult(BitMatrix bits, ResultPoint[] points) {
this.bits = bits;
this.points = points;
}
public BitMatrix getBits() {
return bits;
}
public ResultPoint[] getPoints() {
return points;
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 2008 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.common;
/**
* Superclass of classes encapsulating types ECIs, according to "Extended Channel Interpretations"
* 5.3 of ISO 18004.
*
* @author Sean Owen
*/
public abstract class ECI {
private final int value;
ECI(int value) {
this.value = value;
}
public int getValue() {
return value;
}
/**
* @param value ECI value
* @return {@link ECI} representing ECI of given value, or null if it is legal but unsupported
* @throws IllegalArgumentException if ECI value is invalid
*/
public static ECI getECIByValue(int value) {
if (value < 0 || value > 999999) {
throw new IllegalArgumentException("Bad ECI value: " + value);
}
if (value < 900) { // Character set ECIs use 000000 - 000899
return CharacterSetECI.getCharacterSetECIByValue(value);
}
return null;
}
}

View File

@ -0,0 +1,191 @@
/*
* Copyright (C) 2010 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.common;
import java.util.Hashtable;
import com.google.zxing.DecodeHintType;
/**
* Common string-related functions.
*
* @author Sean Owen
*/
public final class StringUtils {
private static final String PLATFORM_DEFAULT_ENCODING =
System.getProperty("file.encoding");
public static final String SHIFT_JIS = "SJIS";
private static final String EUC_JP = "EUC_JP";
private static final String UTF8 = "UTF8";
private static final String ISO88591 = "ISO8859_1";
private static final boolean ASSUME_SHIFT_JIS =
SHIFT_JIS.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING) ||
EUC_JP.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING);
private StringUtils() {}
/**
* @param bytes bytes encoding a string, whose encoding should be guessed
* @param hints decode hints if applicable
* @return name of guessed encoding; at the moment will only guess one of:
* {@link #SHIFT_JIS}, {@link #UTF8}, {@link #ISO88591}, or the platform
* default encoding if none of these can possibly be correct
*/
public static String guessEncoding(byte[] bytes, Hashtable hints) {
if (hints != null) {
String characterSet = (String) hints.get(DecodeHintType.CHARACTER_SET);
if (characterSet != null) {
return characterSet;
}
}
// Does it start with the UTF-8 byte order mark? then guess it's UTF-8
if (bytes.length > 3 &&
bytes[0] == (byte) 0xEF &&
bytes[1] == (byte) 0xBB &&
bytes[2] == (byte) 0xBF) {
return UTF8;
}
// For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
// which should be by far the most common encodings. ISO-8859-1
// should not have bytes in the 0x80 - 0x9F range, while Shift_JIS
// uses this as a first byte of a two-byte character. If we see this
// followed by a valid second byte in Shift_JIS, assume it is Shift_JIS.
// If we see something else in that second byte, we'll make the risky guess
// that it's UTF-8.
int length = bytes.length;
boolean canBeISO88591 = true;
boolean canBeShiftJIS = true;
boolean canBeUTF8 = true;
int utf8BytesLeft = 0;
int maybeDoubleByteCount = 0;
int maybeSingleByteKatakanaCount = 0;
boolean sawLatin1Supplement = false;
boolean sawUTF8Start = false;
boolean lastWasPossibleDoubleByteStart = false;
for (int i = 0;
i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8);
i++) {
int value = bytes[i] & 0xFF;
// UTF-8 stuff
if (value >= 0x80 && value <= 0xBF) {
if (utf8BytesLeft > 0) {
utf8BytesLeft--;
}
} else {
if (utf8BytesLeft > 0) {
canBeUTF8 = false;
}
if (value >= 0xC0 && value <= 0xFD) {
sawUTF8Start = true;
int valueCopy = value;
while ((valueCopy & 0x40) != 0) {
utf8BytesLeft++;
valueCopy <<= 1;
}
}
}
// ISO-8859-1 stuff
if ((value == 0xC2 || value == 0xC3) && i < length - 1) {
// This is really a poor hack. The slightly more exotic characters people might want to put in
// a QR Code, by which I mean the Latin-1 supplement characters (e.g. u-umlaut) have encodings
// that start with 0xC2 followed by [0xA0,0xBF], or start with 0xC3 followed by [0x80,0xBF].
int nextValue = bytes[i + 1] & 0xFF;
if (nextValue <= 0xBF &&
((value == 0xC2 && nextValue >= 0xA0) || (value == 0xC3 && nextValue >= 0x80))) {
sawLatin1Supplement = true;
}
}
if (value >= 0x7F && value <= 0x9F) {
canBeISO88591 = false;
}
// Shift_JIS stuff
if (value >= 0xA1 && value <= 0xDF) {
// count the number of characters that might be a Shift_JIS single-byte Katakana character
if (!lastWasPossibleDoubleByteStart) {
maybeSingleByteKatakanaCount++;
}
}
if (!lastWasPossibleDoubleByteStart &&
((value >= 0xF0 && value <= 0xFF) || value == 0x80 || value == 0xA0)) {
canBeShiftJIS = false;
}
if (((value >= 0x81 && value <= 0x9F) || (value >= 0xE0 && value <= 0xEF))) {
// These start double-byte characters in Shift_JIS. Let's see if it's followed by a valid
// second byte.
if (lastWasPossibleDoubleByteStart) {
// If we just checked this and the last byte for being a valid double-byte
// char, don't check starting on this byte. If this and the last byte
// formed a valid pair, then this shouldn't be checked to see if it starts
// a double byte pair of course.
lastWasPossibleDoubleByteStart = false;
} else {
// ... otherwise do check to see if this plus the next byte form a valid
// double byte pair encoding a character.
lastWasPossibleDoubleByteStart = true;
if (i >= bytes.length - 1) {
canBeShiftJIS = false;
} else {
int nextValue = bytes[i + 1] & 0xFF;
if (nextValue < 0x40 || nextValue > 0xFC) {
canBeShiftJIS = false;
} else {
maybeDoubleByteCount++;
}
// There is some conflicting information out there about which bytes can follow which in
// double-byte Shift_JIS characters. The rule above seems to be the one that matches practice.
}
}
} else {
lastWasPossibleDoubleByteStart = false;
}
}
if (utf8BytesLeft > 0) {
canBeUTF8 = false;
}
// Easy -- if assuming Shift_JIS and no evidence it can't be, done
if (canBeShiftJIS && ASSUME_SHIFT_JIS) {
return SHIFT_JIS;
}
if (canBeUTF8 && sawUTF8Start) {
return UTF8;
}
// Distinguishing Shift_JIS and ISO-8859-1 can be a little tough. The crude heuristic is:
// - If we saw
// - at least 3 bytes that starts a double-byte value (bytes that are rare in ISO-8859-1), or
// - over 5% of bytes could be single-byte Katakana (also rare in ISO-8859-1),
// - and, saw no sequences that are invalid in Shift_JIS, then we conclude Shift_JIS
if (canBeShiftJIS && (maybeDoubleByteCount >= 3 || 20 * maybeSingleByteKatakanaCount > length)) {
return SHIFT_JIS;
}
// Otherwise, we default to ISO-8859-1 unless we know it can't be
if (!sawLatin1Supplement && canBeISO88591) {
return ISO88591;
}
// Otherwise, we take a wild guess with platform encoding
return PLATFORM_DEFAULT_ENCODING;
}
}

View File

@ -0,0 +1,139 @@
/*
* Copyright 2007 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.common.reedsolomon;
/**
* <p>This class contains utility methods for performing mathematical operations over
* the Galois Field GF(256). Operations use a given primitive polynomial in calculations.</p>
*
* <p>Throughout this package, elements of GF(256) are represented as an <code>int</code>
* for convenience and speed (but at the cost of memory).
* Only the bottom 8 bits are really used.</p>
*
* @author Sean Owen
*/
public final class GF256 {
public static final GF256 QR_CODE_FIELD = new GF256(0x011D); // x^8 + x^4 + x^3 + x^2 + 1
public static final GF256 DATA_MATRIX_FIELD = new GF256(0x012D); // x^8 + x^5 + x^3 + x^2 + 1
private final int[] expTable;
private final int[] logTable;
private final GF256Poly zero;
private final GF256Poly one;
/**
* Create a representation of GF(256) using the given primitive polynomial.
*
* @param primitive irreducible polynomial whose coefficients are represented by
* the bits of an int, where the least-significant bit represents the constant
* coefficient
*/
private GF256(int primitive) {
expTable = new int[256];
logTable = new int[256];
int x = 1;
for (int i = 0; i < 256; i++) {
expTable[i] = x;
x <<= 1; // x = x * 2; we're assuming the generator alpha is 2
if (x >= 0x100) {
x ^= primitive;
}
}
for (int i = 0; i < 255; i++) {
logTable[expTable[i]] = i;
}
// logTable[0] == 0 but this should never be used
zero = new GF256Poly(this, new int[]{0});
one = new GF256Poly(this, new int[]{1});
}
GF256Poly getZero() {
return zero;
}
GF256Poly getOne() {
return one;
}
/**
* @return the monomial representing coefficient * x^degree
*/
GF256Poly buildMonomial(int degree, int coefficient) {
if (degree < 0) {
throw new IllegalArgumentException();
}
if (coefficient == 0) {
return zero;
}
int[] coefficients = new int[degree + 1];
coefficients[0] = coefficient;
return new GF256Poly(this, coefficients);
}
/**
* Implements both addition and subtraction -- they are the same in GF(256).
*
* @return sum/difference of a and b
*/
static int addOrSubtract(int a, int b) {
return a ^ b;
}
/**
* @return 2 to the power of a in GF(256)
*/
int exp(int a) {
return expTable[a];
}
/**
* @return base 2 log of a in GF(256)
*/
int log(int a) {
if (a == 0) {
throw new IllegalArgumentException();
}
return logTable[a];
}
/**
* @return multiplicative inverse of a
*/
int inverse(int a) {
if (a == 0) {
throw new ArithmeticException();
}
return expTable[255 - logTable[a]];
}
/**
* @param a
* @param b
* @return product of a and b in GF(256)
*/
int multiply(int a, int b) {
if (a == 0 || b == 0) {
return 0;
}
int logSum = logTable[a] + logTable[b];
// index is a sped-up alternative to logSum % 255 since sum
// is in [0,510]. Thanks to jmsachs for the idea
return expTable[(logSum & 0xFF) + (logSum >>> 8)];
}
}

View File

@ -0,0 +1,263 @@
/*
* Copyright 2007 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.common.reedsolomon;
/**
* <p>Represents a polynomial whose coefficients are elements of GF(256).
* Instances of this class are immutable.</p>
*
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
* port of his C++ Reed-Solomon implementation.</p>
*
* @author Sean Owen
*/
final class GF256Poly {
private final GF256 field;
private final int[] coefficients;
/**
* @param field the {@link GF256} instance representing the field to use
* to perform computations
* @param coefficients coefficients as ints representing elements of GF(256), arranged
* from most significant (highest-power term) coefficient to least significant
* @throws IllegalArgumentException if argument is null or empty,
* or if leading coefficient is 0 and this is not a
* constant polynomial (that is, it is not the monomial "0")
*/
GF256Poly(GF256 field, int[] coefficients) {
if (coefficients == null || coefficients.length == 0) {
throw new IllegalArgumentException();
}
this.field = field;
int coefficientsLength = coefficients.length;
if (coefficientsLength > 1 && coefficients[0] == 0) {
// Leading term must be non-zero for anything except the constant polynomial "0"
int firstNonZero = 1;
while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {
firstNonZero++;
}
if (firstNonZero == coefficientsLength) {
this.coefficients = field.getZero().coefficients;
} else {
this.coefficients = new int[coefficientsLength - firstNonZero];
System.arraycopy(coefficients,
firstNonZero,
this.coefficients,
0,
this.coefficients.length);
}
} else {
this.coefficients = coefficients;
}
}
int[] getCoefficients() {
return coefficients;
}
/**
* @return degree of this polynomial
*/
int getDegree() {
return coefficients.length - 1;
}
/**
* @return true iff this polynomial is the monomial "0"
*/
boolean isZero() {
return coefficients[0] == 0;
}
/**
* @return coefficient of x^degree term in this polynomial
*/
int getCoefficient(int degree) {
return coefficients[coefficients.length - 1 - degree];
}
/**
* @return evaluation of this polynomial at a given point
*/
int evaluateAt(int a) {
if (a == 0) {
// Just return the x^0 coefficient
return getCoefficient(0);
}
int size = coefficients.length;
if (a == 1) {
// Just the sum of the coefficients
int result = 0;
for (int i = 0; i < size; i++) {
result = GF256.addOrSubtract(result, coefficients[i]);
}
return result;
}
int result = coefficients[0];
for (int i = 1; i < size; i++) {
result = GF256.addOrSubtract(field.multiply(a, result), coefficients[i]);
}
return result;
}
GF256Poly addOrSubtract(GF256Poly other) {
if (!field.equals(other.field)) {
throw new IllegalArgumentException("GF256Polys do not have same GF256 field");
}
if (isZero()) {
return other;
}
if (other.isZero()) {
return this;
}
int[] smallerCoefficients = this.coefficients;
int[] largerCoefficients = other.coefficients;
if (smallerCoefficients.length > largerCoefficients.length) {
int[] temp = smallerCoefficients;
smallerCoefficients = largerCoefficients;
largerCoefficients = temp;
}
int[] sumDiff = new int[largerCoefficients.length];
int lengthDiff = largerCoefficients.length - smallerCoefficients.length;
// Copy high-order terms only found in higher-degree polynomial's coefficients
System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
for (int i = lengthDiff; i < largerCoefficients.length; i++) {
sumDiff[i] = GF256.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
}
return new GF256Poly(field, sumDiff);
}
GF256Poly multiply(GF256Poly other) {
if (!field.equals(other.field)) {
throw new IllegalArgumentException("GF256Polys do not have same GF256 field");
}
if (isZero() || other.isZero()) {
return field.getZero();
}
int[] aCoefficients = this.coefficients;
int aLength = aCoefficients.length;
int[] bCoefficients = other.coefficients;
int bLength = bCoefficients.length;
int[] product = new int[aLength + bLength - 1];
for (int i = 0; i < aLength; i++) {
int aCoeff = aCoefficients[i];
for (int j = 0; j < bLength; j++) {
product[i + j] = GF256.addOrSubtract(product[i + j],
field.multiply(aCoeff, bCoefficients[j]));
}
}
return new GF256Poly(field, product);
}
GF256Poly multiply(int scalar) {
if (scalar == 0) {
return field.getZero();
}
if (scalar == 1) {
return this;
}
int size = coefficients.length;
int[] product = new int[size];
for (int i = 0; i < size; i++) {
product[i] = field.multiply(coefficients[i], scalar);
}
return new GF256Poly(field, product);
}
GF256Poly multiplyByMonomial(int degree, int coefficient) {
if (degree < 0) {
throw new IllegalArgumentException();
}
if (coefficient == 0) {
return field.getZero();
}
int size = coefficients.length;
int[] product = new int[size + degree];
for (int i = 0; i < size; i++) {
product[i] = field.multiply(coefficients[i], coefficient);
}
return new GF256Poly(field, product);
}
GF256Poly[] divide(GF256Poly other) {
if (!field.equals(other.field)) {
throw new IllegalArgumentException("GF256Polys do not have same GF256 field");
}
if (other.isZero()) {
throw new IllegalArgumentException("Divide by 0");
}
GF256Poly quotient = field.getZero();
GF256Poly remainder = this;
int denominatorLeadingTerm = other.getCoefficient(other.getDegree());
int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm);
while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) {
int degreeDifference = remainder.getDegree() - other.getDegree();
int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm);
GF256Poly term = other.multiplyByMonomial(degreeDifference, scale);
GF256Poly iterationQuotient = field.buildMonomial(degreeDifference, scale);
quotient = quotient.addOrSubtract(iterationQuotient);
remainder = remainder.addOrSubtract(term);
}
return new GF256Poly[] { quotient, remainder };
}
public String toString() {
StringBuffer result = new StringBuffer(8 * getDegree());
for (int degree = getDegree(); degree >= 0; degree--) {
int coefficient = getCoefficient(degree);
if (coefficient != 0) {
if (coefficient < 0) {
result.append(" - ");
coefficient = -coefficient;
} else {
if (result.length() > 0) {
result.append(" + ");
}
}
if (degree == 0 || coefficient != 1) {
int alphaPower = field.log(coefficient);
if (alphaPower == 0) {
result.append('1');
} else if (alphaPower == 1) {
result.append('a');
} else {
result.append("a^");
result.append(alphaPower);
}
}
if (degree != 0) {
if (degree == 1) {
result.append('x');
} else {
result.append("x^");
result.append(degree);
}
}
}
}
return result.toString();
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2008 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.common.reedsolomon;
import java.util.Vector;
/**
* <p>Implements Reed-Solomon enbcoding, as the name implies.</p>
*
* @author Sean Owen
* @author William Rucklidge
*/
public final class ReedSolomonEncoder {
private final GF256 field;
private final Vector cachedGenerators;
public ReedSolomonEncoder(GF256 field) {
if (!GF256.QR_CODE_FIELD.equals(field)) {
throw new IllegalArgumentException("Only QR Code is supported at this time");
}
this.field = field;
this.cachedGenerators = new Vector();
cachedGenerators.addElement(new GF256Poly(field, new int[] { 1 }));
}
private GF256Poly buildGenerator(int degree) {
if (degree >= cachedGenerators.size()) {
GF256Poly lastGenerator = (GF256Poly) cachedGenerators.elementAt(cachedGenerators.size() - 1);
for (int d = cachedGenerators.size(); d <= degree; d++) {
GF256Poly nextGenerator = lastGenerator.multiply(new GF256Poly(field, new int[] { 1, field.exp(d - 1) }));
cachedGenerators.addElement(nextGenerator);
lastGenerator = nextGenerator;
}
}
return (GF256Poly) cachedGenerators.elementAt(degree);
}
public void encode(int[] toEncode, int ecBytes) {
if (ecBytes == 0) {
throw new IllegalArgumentException("No error correction bytes");
}
int dataBytes = toEncode.length - ecBytes;
if (dataBytes <= 0) {
throw new IllegalArgumentException("No data bytes provided");
}
GF256Poly generator = buildGenerator(ecBytes);
int[] infoCoefficients = new int[dataBytes];
System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes);
GF256Poly info = new GF256Poly(field, infoCoefficients);
info = info.multiplyByMonomial(ecBytes, 1);
GF256Poly remainder = info.divide(generator)[1];
int[] coefficients = remainder.getCoefficients();
int numZeroCoefficients = ecBytes - coefficients.length;
for (int i = 0; i < numZeroCoefficients; i++) {
toEncode[dataBytes + i] = 0;
}
System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length);
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2007 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.common.reedsolomon;
/**
* <p>Thrown when an exception occurs during Reed-Solomon decoding, such as when
* there are too many errors to correct.</p>
*
* @author Sean Owen
*/
public final class ReedSolomonException extends Exception {
public ReedSolomonException(String message) {
super(message);
}
}

View File

@ -0,0 +1,446 @@
/*
* Copyright 2007 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.datamatrix.decoder;
import com.google.zxing.FormatException;
import com.google.zxing.common.BitMatrix;
/**
* @author bbrown@google.com (Brian Brown)
*/
final class BitMatrixParser {
private final BitMatrix mappingBitMatrix;
private final BitMatrix readMappingMatrix;
private final Version version;
/**
* @param bitMatrix {@link BitMatrix} to parse
* @throws FormatException if dimension is < 10 or > 144 or not 0 mod 2
*/
BitMatrixParser(BitMatrix bitMatrix) throws FormatException {
int dimension = bitMatrix.getHeight();
if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) {
throw FormatException.getFormatInstance();
}
version = readVersion(bitMatrix);
this.mappingBitMatrix = extractDataRegion(bitMatrix);
// TODO(bbrown): Make this work for rectangular symbols
this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getHeight());
}
/**
* <p>Creates the version object based on the dimension of the original bit matrix from
* the datamatrix code.</p>
*
* <p>See ISO 16022:2006 Table 7 - ECC 200 symbol attributes</p>
*
* @param bitMatrix Original {@link BitMatrix} including alignment patterns
* @return {@link Version} encapsulating the Data Matrix Code's "version"
* @throws FormatException if the dimensions of the mapping matrix are not valid
* Data Matrix dimensions.
*/
Version readVersion(BitMatrix bitMatrix) throws FormatException {
if (version != null) {
return version;
}
// TODO(bbrown): make this work for rectangular dimensions as well.
int numRows = bitMatrix.getHeight();
int numColumns = numRows;
return Version.getVersionForDimensions(numRows, numColumns);
}
/**
* <p>Reads the bits in the {@link BitMatrix} representing the mapping matrix (No alignment patterns)
* in the correct order in order to reconstitute the codewords bytes contained within the
* Data Matrix Code.</p>
*
* @return bytes encoded within the Data Matrix Code
* @throws FormatException if the exact number of bytes expected is not read
*/
byte[] readCodewords() throws FormatException {
byte[] result = new byte[version.getTotalCodewords()];
int resultOffset = 0;
int row = 4;
int column = 0;
// TODO(bbrown): Data Matrix can be rectangular, assuming square for now
int numRows = mappingBitMatrix.getHeight();
int numColumns = numRows;
boolean corner1Read = false;
boolean corner2Read = false;
boolean corner3Read = false;
boolean corner4Read = false;
// Read all of the codewords
do {
// Check the four corner cases
if ((row == numRows) && (column == 0) && !corner1Read) {
result[resultOffset++] = (byte) readCorner1(numRows, numColumns);
row -= 2;
column +=2;
corner1Read = true;
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) {
result[resultOffset++] = (byte) readCorner2(numRows, numColumns);
row -= 2;
column +=2;
corner2Read = true;
} else if ((row == numRows+4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) {
result[resultOffset++] = (byte) readCorner3(numRows, numColumns);
row -= 2;
column +=2;
corner3Read = true;
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) {
result[resultOffset++] = (byte) readCorner4(numRows, numColumns);
row -= 2;
column +=2;
corner4Read = true;
} else {
// Sweep upward diagonally to the right
do {
if ((row < numRows) && (column >= 0) && !readMappingMatrix.get(column, row)) {
result[resultOffset++] = (byte) readUtah(row, column, numRows, numColumns);
}
row -= 2;
column +=2;
} while ((row >= 0) && (column < numColumns));
row += 1;
column +=3;
// Sweep downward diagonally to the left
do {
if ((row >= 0) && (column < numColumns) && !readMappingMatrix.get(column, row)) {
result[resultOffset++] = (byte) readUtah(row, column, numRows, numColumns);
}
row += 2;
column -=2;
} while ((row < numRows) && (column >= 0));
row += 3;
column +=1;
}
} while ((row < numRows) || (column < numColumns));
if (resultOffset != version.getTotalCodewords()) {
throw FormatException.getFormatInstance();
}
return result;
}
/**
* <p>Reads a bit of the mapping matrix accounting for boundary wrapping.</p>
*
* @param row Row to read in the mapping matrix
* @param column Column to read in the mapping matrix
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return value of the given bit in the mapping matrix
*/
boolean readModule(int row, int column, int numRows, int numColumns) {
// Adjust the row and column indices based on boundary wrapping
if (row < 0) {
row += numRows;
column += 4 - ((numRows + 4) & 0x07);
}
if (column < 0) {
column += numColumns;
row += 4 - ((numColumns + 4) & 0x07);
}
readMappingMatrix.set(column, row);
return mappingBitMatrix.get(column, row);
}
/**
* <p>Reads the 8 bits of the standard Utah-shaped pattern.</p>
*
* <p>See ISO 16022:2006, 5.8.1 Figure 6</p>
*
* @param row Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern
* @param column Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the utah shape
*/
int readUtah(int row, int column, int numRows, int numColumns) {
int currentByte = 0;
if (readModule(row - 2, column - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 2, column - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 1, column - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 1, column - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row - 1, column, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row, column - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row, column - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(row, column, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
/**
* <p>Reads the 8 bits of the special corner condition 1.</p>
*
* <p>See ISO 16022:2006, Figure F.3</p>
*
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the Corner condition 1
*/
int readCorner1(int numRows, int numColumns) {
int currentByte = 0;
if (readModule(numRows - 1, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(2, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(3, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
/**
* <p>Reads the 8 bits of the special corner condition 2.</p>
*
* <p>See ISO 16022:2006, Figure F.4</p>
*
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the Corner condition 2
*/
int readCorner2(int numRows, int numColumns) {
int currentByte = 0;
if (readModule(numRows - 3, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 2, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 4, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 3, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
/**
* <p>Reads the 8 bits of the special corner condition 3.</p>
*
* <p>See ISO 16022:2006, Figure F.5</p>
*
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the Corner condition 3
*/
int readCorner3(int numRows, int numColumns) {
int currentByte = 0;
if (readModule(numRows - 1, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 3, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 3, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
/**
* <p>Reads the 8 bits of the special corner condition 4.</p>
*
* <p>See ISO 16022:2006, Figure F.6</p>
*
* @param numRows Number of rows in the mapping matrix
* @param numColumns Number of columns in the mapping matrix
* @return byte from the Corner condition 4
*/
int readCorner4(int numRows, int numColumns) {
int currentByte = 0;
if (readModule(numRows - 3, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 2, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(numRows - 1, 0, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 2, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(0, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(1, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(2, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
currentByte <<= 1;
if (readModule(3, numColumns - 1, numRows, numColumns)) {
currentByte |= 1;
}
return currentByte;
}
/**
* <p>Extracts the data region from a {@link BitMatrix} that contains
* alignment patterns.</p>
*
* @param bitMatrix Original {@link BitMatrix} with alignment patterns
* @return BitMatrix that has the alignment patterns removed
*/
BitMatrix extractDataRegion(BitMatrix bitMatrix) {
int symbolSizeRows = version.getSymbolSizeRows();
int symbolSizeColumns = version.getSymbolSizeColumns();
// TODO(bbrown): Make this work with rectangular codes
if (bitMatrix.getHeight() != symbolSizeRows) {
throw new IllegalArgumentException("Dimension of bitMarix must match the version size");
}
int dataRegionSizeRows = version.getDataRegionSizeRows();
int dataRegionSizeColumns = version.getDataRegionSizeColumns();
int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows;
int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns;
int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows;
//int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;
// TODO(bbrown): Make this work with rectangular codes
BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionRow);
for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {
int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows;
for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {
int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns;
for (int i = 0; i < dataRegionSizeRows; ++i) {
int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i;
int writeRowOffset = dataRegionRowOffset + i;
for (int j = 0; j < dataRegionSizeColumns; ++j) {
int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j;
if (bitMatrix.get(readColumnOffset, readRowOffset)) {
int writeColumnOffset = dataRegionColumnOffset + j;
bitMatrixWithoutAlignment.set(writeColumnOffset, writeRowOffset);
}
}
}
}
}
return bitMatrixWithoutAlignment;
}
}

View File

@ -0,0 +1,118 @@
/*
* Copyright 2008 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.datamatrix.decoder;
/**
* <p>Encapsulates a block of data within a Data Matrix Code. Data Matrix Codes may split their data into
* multiple blocks, each of which is a unit of data and error-correction codewords. Each
* is represented by an instance of this class.</p>
*
* @author bbrown@google.com (Brian Brown)
*/
final class DataBlock {
private final int numDataCodewords;
private final byte[] codewords;
private DataBlock(int numDataCodewords, byte[] codewords) {
this.numDataCodewords = numDataCodewords;
this.codewords = codewords;
}
/**
* <p>When Data Matrix Codes use multiple data blocks, they actually interleave the bytes of each of them.
* That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
* method will separate the data into original blocks.</p>
*
* @param rawCodewords bytes as read directly from the Data Matrix Code
* @param version version of the Data Matrix Code
* @return {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the
* Data Matrix Code
*/
static DataBlock[] getDataBlocks(byte[] rawCodewords,
Version version) {
// Figure out the number and size of data blocks used by this version
Version.ECBlocks ecBlocks = version.getECBlocks();
// First count the total number of data blocks
int totalBlocks = 0;
Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();
for (int i = 0; i < ecBlockArray.length; i++) {
totalBlocks += ecBlockArray[i].getCount();
}
// Now establish DataBlocks of the appropriate size and number of data codewords
DataBlock[] result = new DataBlock[totalBlocks];
int numResultBlocks = 0;
for (int j = 0; j < ecBlockArray.length; j++) {
Version.ECB ecBlock = ecBlockArray[j];
for (int i = 0; i < ecBlock.getCount(); i++) {
int numDataCodewords = ecBlock.getDataCodewords();
int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords;
result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);
}
}
// All blocks have the same amount of data, except that the last n
// (where n may be 0) have 1 less byte. Figure out where these start.
// TODO(bbrown): There is only one case where there is a difference for Data Matrix for size 144
int longerBlocksTotalCodewords = result[0].codewords.length;
//int shorterBlocksTotalCodewords = longerBlocksTotalCodewords - 1;
int longerBlocksNumDataCodewords = longerBlocksTotalCodewords - ecBlocks.getECCodewords();
int shorterBlocksNumDataCodewords = longerBlocksNumDataCodewords - 1;
// The last elements of result may be 1 element shorter for 144 matrix
// first fill out as many elements as all of them have minus 1
int rawCodewordsOffset = 0;
for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
for (int j = 0; j < numResultBlocks; j++) {
result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
}
}
// Fill out the last data block in the longer ones
boolean specialVersion = version.getVersionNumber() == 24;
int numLongerBlocks = specialVersion ? 8 : numResultBlocks;
for (int j = 0; j < numLongerBlocks; j++) {
result[j].codewords[longerBlocksNumDataCodewords - 1] = rawCodewords[rawCodewordsOffset++];
}
// Now add in error correction blocks
int max = result[0].codewords.length;
for (int i = longerBlocksNumDataCodewords; i < max; i++) {
for (int j = 0; j < numResultBlocks; j++) {
int iOffset = (specialVersion && j > 7) ? i - 1 : i;
result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
}
}
if (rawCodewordsOffset != rawCodewords.length) {
throw new IllegalArgumentException();
}
return result;
}
int getNumDataCodewords() {
return numDataCodewords;
}
byte[] getCodewords() {
return codewords;
}
}

View File

@ -0,0 +1,242 @@
/*
* Copyright 2007 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.datamatrix.decoder;
import com.google.zxing.FormatException;
/**
* The Version object encapsulates attributes about a particular
* size Data Matrix Code.
*
* @author bbrown@google.com (Brian Brown)
*/
public final class Version {
private static final Version[] VERSIONS = buildVersions();
private final int versionNumber;
private final int symbolSizeRows;
private final int symbolSizeColumns;
private final int dataRegionSizeRows;
private final int dataRegionSizeColumns;
private final ECBlocks ecBlocks;
private final int totalCodewords;
private Version(int versionNumber,
int symbolSizeRows,
int symbolSizeColumns,
int dataRegionSizeRows,
int dataRegionSizeColumns,
ECBlocks ecBlocks) {
this.versionNumber = versionNumber;
this.symbolSizeRows = symbolSizeRows;
this.symbolSizeColumns = symbolSizeColumns;
this.dataRegionSizeRows = dataRegionSizeRows;
this.dataRegionSizeColumns = dataRegionSizeColumns;
this.ecBlocks = ecBlocks;
// Calculate the total number of codewords
int total = 0;
int ecCodewords = ecBlocks.getECCodewords();
ECB[] ecbArray = ecBlocks.getECBlocks();
for (int i = 0; i < ecbArray.length; i++) {
ECB ecBlock = ecbArray[i];
total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords);
}
this.totalCodewords = total;
}
public int getVersionNumber() {
return versionNumber;
}
public int getSymbolSizeRows() {
return symbolSizeRows;
}
public int getSymbolSizeColumns() {
return symbolSizeColumns;
}
public int getDataRegionSizeRows() {
return dataRegionSizeRows;
}
public int getDataRegionSizeColumns() {
return dataRegionSizeColumns;
}
public int getTotalCodewords() {
return totalCodewords;
}
ECBlocks getECBlocks() {
return ecBlocks;
}
/**
* <p>Deduces version information from Data Matrix dimensions.</p>
*
* @param numRows Number of rows in modules
* @param numColumns Number of columns in modules
* @return {@link Version} for a Data Matrix Code of those dimensions
* @throws FormatException if dimensions do correspond to a valid Data Matrix size
*/
public static Version getVersionForDimensions(int numRows, int numColumns) throws FormatException {
if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) {
throw FormatException.getFormatInstance();
}
// TODO(bbrown): This is doing a linear search through the array of versions.
// If we interleave the rectangular versions with the square versions we could
// do a binary search.
int numVersions = VERSIONS.length;
for (int i = 0; i < numVersions; ++i){
Version version = VERSIONS[i];
if (version.symbolSizeRows == numRows && version.symbolSizeColumns == numColumns) {
return version;
}
}
throw FormatException.getFormatInstance();
}
/**
* <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
* use blocks of differing sizes within one version, so, this encapsulates the parameters for
* each set of blocks. It also holds the number of error-correction codewords per block since it
* will be the same across all blocks within one version.</p>
*/
static final class ECBlocks {
private final int ecCodewords;
private final ECB[] ecBlocks;
private ECBlocks(int ecCodewords, ECB ecBlocks) {
this.ecCodewords = ecCodewords;
this.ecBlocks = new ECB[] { ecBlocks };
}
private ECBlocks(int ecCodewords, ECB ecBlocks1, ECB ecBlocks2) {
this.ecCodewords = ecCodewords;
this.ecBlocks = new ECB[] { ecBlocks1, ecBlocks2 };
}
int getECCodewords() {
return ecCodewords;
}
ECB[] getECBlocks() {
return ecBlocks;
}
}
/**
* <p>Encapsualtes the parameters for one error-correction block in one symbol version.
* This includes the number of data codewords, and the number of times a block with these
* parameters is used consecutively in the Data Matrix code version's format.</p>
*/
static final class ECB {
private final int count;
private final int dataCodewords;
private ECB(int count, int dataCodewords) {
this.count = count;
this.dataCodewords = dataCodewords;
}
int getCount() {
return count;
}
int getDataCodewords() {
return dataCodewords;
}
}
public String toString() {
return String.valueOf(versionNumber);
}
/**
* See ISO 16022:2006 5.5.1 Table 7
*/
private static Version[] buildVersions() {
return new Version[]{
new Version(1, 10, 10, 8, 8,
new ECBlocks(5, new ECB(1, 3))),
new Version(2, 12, 12, 10, 10,
new ECBlocks(7, new ECB(1, 5))),
new Version(3, 14, 14, 12, 12,
new ECBlocks(10, new ECB(1, 8))),
new Version(4, 16, 16, 14, 14,
new ECBlocks(12, new ECB(1, 12))),
new Version(5, 18, 18, 16, 16,
new ECBlocks(14, new ECB(1, 18))),
new Version(6, 20, 20, 18, 18,
new ECBlocks(18, new ECB(1, 22))),
new Version(7, 22, 22, 20, 20,
new ECBlocks(20, new ECB(1, 30))),
new Version(8, 24, 24, 22, 22,
new ECBlocks(24, new ECB(1, 36))),
new Version(9, 26, 26, 24, 24,
new ECBlocks(28, new ECB(1, 44))),
new Version(10, 32, 32, 14, 14,
new ECBlocks(36, new ECB(1, 62))),
new Version(11, 36, 36, 16, 16,
new ECBlocks(42, new ECB(1, 86))),
new Version(12, 40, 40, 18, 18,
new ECBlocks(48, new ECB(1, 114))),
new Version(13, 44, 44, 20, 20,
new ECBlocks(56, new ECB(1, 144))),
new Version(14, 48, 48, 22, 22,
new ECBlocks(68, new ECB(1, 174))),
new Version(15, 52, 52, 24, 24,
new ECBlocks(42, new ECB(2, 102))),
new Version(16, 64, 64, 14, 14,
new ECBlocks(56, new ECB(2, 140))),
new Version(17, 72, 72, 16, 16,
new ECBlocks(36, new ECB(4, 92))),
new Version(18, 80, 80, 18, 18,
new ECBlocks(48, new ECB(4, 114))),
new Version(19, 88, 88, 20, 20,
new ECBlocks(56, new ECB(4, 144))),
new Version(20, 96, 96, 22, 22,
new ECBlocks(68, new ECB(4, 174))),
new Version(21, 104, 104, 24, 24,
new ECBlocks(56, new ECB(6, 136))),
new Version(22, 120, 120, 18, 18,
new ECBlocks(68, new ECB(6, 175))),
new Version(23, 132, 132, 20, 20,
new ECBlocks(62, new ECB(8, 163))),
new Version(24, 144, 144, 22, 22,
new ECBlocks(62, new ECB(8, 156), new ECB(2, 155))),
new Version(25, 8, 18, 6, 16,
new ECBlocks(7, new ECB(1, 5))),
new Version(26, 8, 32, 6, 14,
new ECBlocks(11, new ECB(1, 10))),
new Version(27, 12, 26, 10, 24,
new ECBlocks(14, new ECB(1, 16))),
new Version(28, 12, 36, 10, 16,
new ECBlocks(18, new ECB(1, 22))),
new Version(29, 16, 36, 10, 16,
new ECBlocks(24, new ECB(1, 32))),
new Version(30, 16, 48, 14, 22,
new ECBlocks(28, new ECB(1, 49)))
};
}
}

View File

@ -0,0 +1,106 @@
/*
* Copyright 2008 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.qrcode;
import java.util.Hashtable;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.Writer;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import com.google.zxing.qrcode.encoder.Encoder;
import com.google.zxing.qrcode.encoder.QRCode;
/**
* This object renders a QR Code as a BitMatrix 2D array of greyscale values.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class QRCodeWriter implements Writer {
private static final int QUIET_ZONE_SIZE = 4;
public BitMatrix encode(String contents, BarcodeFormat format, int multiple) throws WriterException {
return encode(contents, format, multiple, null);
}
public BitMatrix encode(String contents, BarcodeFormat format, int multiple, Hashtable hints) throws WriterException {
if (contents == null || contents.length() == 0) {
throw new IllegalArgumentException("Found empty contents");
}
if (format != BarcodeFormat.QR_CODE) {
throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
}
ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
if (hints != null) {
ErrorCorrectionLevel requestedECLevel = (ErrorCorrectionLevel) hints.get(EncodeHintType.ERROR_CORRECTION);
if (requestedECLevel != null) {
errorCorrectionLevel = requestedECLevel;
}
}
QRCode code = new QRCode();
Encoder.encode(contents, errorCorrectionLevel, hints, code);
return renderResult(code, multiple);
}
// Note that the input matrix uses 0 == white, 1 == black, while the output
// matrix uses
// 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
private static BitMatrix renderResult(QRCode code, int multiple) {
ByteMatrix input = code.getMatrix();
int inputWidth = input.getWidth();
int inputHeight = input.getHeight();
int qrWidth = inputWidth + (QUIET_ZONE_SIZE << 1);
int qrHeight = inputHeight + (QUIET_ZONE_SIZE << 1);
// int outputWidth = Math.max(width, qrWidth);
// int outputHeight = Math.max(height, qrHeight);
// int multiple = Math.min(outputWidth / qrWidth, outputHeight /
// qrHeight);
// Padding includes both the quiet zone and the extra white pixels to
// accommodate the requested
// dimensions. For example, if input is 25x25 the QR will be 33x33
// including the quiet zone.
// If the requested size is 200x160, the multiple will be 4, for a QR of
// 132x132. These will
// handle all the padding from 100x100 (the actual QR) up to 200x160.
int leftPadding = QUIET_ZONE_SIZE * multiple;
int topPadding = QUIET_ZONE_SIZE * multiple;
BitMatrix output = new BitMatrix(qrWidth * multiple, qrHeight * multiple);
for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
// Write the contents of this row of the barcode
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
if (input.get(inputX, inputY) == 1) {
output.setRegion(outputX, outputY, multiple, multiple);
}
}
}
return output;
}
}

View File

@ -0,0 +1,203 @@
/*
* Copyright 2007 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.qrcode.decoder;
import com.google.zxing.FormatException;
import com.google.zxing.common.BitMatrix;
/**
* @author Sean Owen
*/
final class BitMatrixParser {
private final BitMatrix bitMatrix;
private Version parsedVersion;
private FormatInformation parsedFormatInfo;
/**
* @param bitMatrix {@link BitMatrix} to parse
* @throws FormatException if dimension is not >= 21 and 1 mod 4
*/
BitMatrixParser(BitMatrix bitMatrix) throws FormatException {
int dimension = bitMatrix.getHeight();
if (dimension < 21 || (dimension & 0x03) != 1) {
throw FormatException.getFormatInstance();
}
this.bitMatrix = bitMatrix;
}
/**
* <p>Reads format information from one of its two locations within the QR Code.</p>
*
* @return {@link FormatInformation} encapsulating the QR Code's format info
* @throws FormatException if both format information locations cannot be parsed as
* the valid encoding of format information
*/
FormatInformation readFormatInformation() throws FormatException {
if (parsedFormatInfo != null) {
return parsedFormatInfo;
}
// Read top-left format info bits
int formatInfoBits1 = 0;
for (int i = 0; i < 6; i++) {
formatInfoBits1 = copyBit(i, 8, formatInfoBits1);
}
// .. and skip a bit in the timing pattern ...
formatInfoBits1 = copyBit(7, 8, formatInfoBits1);
formatInfoBits1 = copyBit(8, 8, formatInfoBits1);
formatInfoBits1 = copyBit(8, 7, formatInfoBits1);
// .. and skip a bit in the timing pattern ...
for (int j = 5; j >= 0; j--) {
formatInfoBits1 = copyBit(8, j, formatInfoBits1);
}
// Read the top-right/bottom-left pattern too
int dimension = bitMatrix.getHeight();
int formatInfoBits2 = 0;
int iMin = dimension - 8;
for (int i = dimension - 1; i >= iMin; i--) {
formatInfoBits2 = copyBit(i, 8, formatInfoBits2);
}
for (int j = dimension - 7; j < dimension; j++) {
formatInfoBits2 = copyBit(8, j, formatInfoBits2);
}
parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2);
if (parsedFormatInfo != null) {
return parsedFormatInfo;
}
throw FormatException.getFormatInstance();
}
/**
* <p>Reads version information from one of its two locations within the QR Code.</p>
*
* @return {@link Version} encapsulating the QR Code's version
* @throws FormatException if both version information locations cannot be parsed as
* the valid encoding of version information
*/
Version readVersion() throws FormatException {
if (parsedVersion != null) {
return parsedVersion;
}
int dimension = bitMatrix.getHeight();
int provisionalVersion = (dimension - 17) >> 2;
if (provisionalVersion <= 6) {
return Version.getVersionForNumber(provisionalVersion);
}
// Read top-right version info: 3 wide by 6 tall
int versionBits = 0;
int ijMin = dimension - 11;
for (int j = 5; j >= 0; j--) {
for (int i = dimension - 9; i >= ijMin; i--) {
versionBits = copyBit(i, j, versionBits);
}
}
parsedVersion = Version.decodeVersionInformation(versionBits);
if (parsedVersion != null && parsedVersion.getDimensionForVersion() == dimension) {
return parsedVersion;
}
// Hmm, failed. Try bottom left: 6 wide by 3 tall
versionBits = 0;
for (int i = 5; i >= 0; i--) {
for (int j = dimension - 9; j >= ijMin; j--) {
versionBits = copyBit(i, j, versionBits);
}
}
parsedVersion = Version.decodeVersionInformation(versionBits);
if (parsedVersion != null && parsedVersion.getDimensionForVersion() == dimension) {
return parsedVersion;
}
throw FormatException.getFormatInstance();
}
private int copyBit(int i, int j, int versionBits) {
return bitMatrix.get(i, j) ? (versionBits << 1) | 0x1 : versionBits << 1;
}
/**
* <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
* correct order in order to reconstitute the codewords bytes contained within the
* QR Code.</p>
*
* @return bytes encoded within the QR Code
* @throws FormatException if the exact number of bytes expected is not read
*/
byte[] readCodewords() throws FormatException {
FormatInformation formatInfo = readFormatInformation();
Version version = readVersion();
// Get the data mask for the format used in this QR Code. This will exclude
// some bits from reading as we wind through the bit matrix.
DataMask dataMask = DataMask.forReference((int) formatInfo.getDataMask());
int dimension = bitMatrix.getHeight();
dataMask.unmaskBitMatrix(bitMatrix, dimension);
BitMatrix functionPattern = version.buildFunctionPattern();
boolean readingUp = true;
byte[] result = new byte[version.getTotalCodewords()];
int resultOffset = 0;
int currentByte = 0;
int bitsRead = 0;
// Read columns in pairs, from right to left
for (int j = dimension - 1; j > 0; j -= 2) {
if (j == 6) {
// Skip whole column with vertical alignment pattern;
// saves time and makes the other code proceed more cleanly
j--;
}
// Read alternatingly from bottom to top then top to bottom
for (int count = 0; count < dimension; count++) {
int i = readingUp ? dimension - 1 - count : count;
for (int col = 0; col < 2; col++) {
// Ignore bits covered by the function pattern
if (!functionPattern.get(j - col, i)) {
// Read a bit
bitsRead++;
currentByte <<= 1;
if (bitMatrix.get(j - col, i)) {
currentByte |= 1;
}
// If we've made a whole byte, save it off
if (bitsRead == 8) {
result[resultOffset++] = (byte) currentByte;
bitsRead = 0;
currentByte = 0;
}
}
}
}
readingUp ^= true; // readingUp = !readingUp; // switch directions
}
if (resultOffset != version.getTotalCodewords()) {
throw FormatException.getFormatInstance();
}
return result;
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright 2007 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.qrcode.decoder;
/**
* <p>Encapsulates a block of data within a QR Code. QR Codes may split their data into
* multiple blocks, each of which is a unit of data and error-correction codewords. Each
* is represented by an instance of this class.</p>
*
* @author Sean Owen
*/
final class DataBlock {
private final int numDataCodewords;
private final byte[] codewords;
private DataBlock(int numDataCodewords, byte[] codewords) {
this.numDataCodewords = numDataCodewords;
this.codewords = codewords;
}
/**
* <p>When QR Codes use multiple data blocks, they are actually interleaved.
* That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
* method will separate the data into original blocks.</p>
*
* @param rawCodewords bytes as read directly from the QR Code
* @param version version of the QR Code
* @param ecLevel error-correction level of the QR Code
* @return {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the
* QR Code
*/
static DataBlock[] getDataBlocks(byte[] rawCodewords,
Version version,
ErrorCorrectionLevel ecLevel) {
if (rawCodewords.length != version.getTotalCodewords()) {
throw new IllegalArgumentException();
}
// Figure out the number and size of data blocks used by this version and
// error correction level
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
// First count the total number of data blocks
int totalBlocks = 0;
Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();
for (int i = 0; i < ecBlockArray.length; i++) {
totalBlocks += ecBlockArray[i].getCount();
}
// Now establish DataBlocks of the appropriate size and number of data codewords
DataBlock[] result = new DataBlock[totalBlocks];
int numResultBlocks = 0;
for (int j = 0; j < ecBlockArray.length; j++) {
Version.ECB ecBlock = ecBlockArray[j];
for (int i = 0; i < ecBlock.getCount(); i++) {
int numDataCodewords = ecBlock.getDataCodewords();
int numBlockCodewords = ecBlocks.getECCodewordsPerBlock() + numDataCodewords;
result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]);
}
}
// All blocks have the same amount of data, except that the last n
// (where n may be 0) have 1 more byte. Figure out where these start.
int shorterBlocksTotalCodewords = result[0].codewords.length;
int longerBlocksStartAt = result.length - 1;
while (longerBlocksStartAt >= 0) {
int numCodewords = result[longerBlocksStartAt].codewords.length;
if (numCodewords == shorterBlocksTotalCodewords) {
break;
}
longerBlocksStartAt--;
}
longerBlocksStartAt++;
int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewordsPerBlock();
// The last elements of result may be 1 element longer;
// first fill out as many elements as all of them have
int rawCodewordsOffset = 0;
for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
for (int j = 0; j < numResultBlocks; j++) {
result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
}
}
// Fill out the last data block in the longer ones
for (int j = longerBlocksStartAt; j < numResultBlocks; j++) {
result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++];
}
// Now add in error correction blocks
int max = result[0].codewords.length;
for (int i = shorterBlocksNumDataCodewords; i < max; i++) {
for (int j = 0; j < numResultBlocks; j++) {
int iOffset = j < longerBlocksStartAt ? i : i + 1;
result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
}
}
return result;
}
int getNumDataCodewords() {
return numDataCodewords;
}
byte[] getCodewords() {
return codewords;
}
}

View File

@ -0,0 +1,155 @@
/*
* Copyright 2007 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.qrcode.decoder;
import com.google.zxing.common.BitMatrix;
/**
* <p>Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
* of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,
* including areas used for finder patterns, timing patterns, etc. These areas should be unused
* after the point they are unmasked anyway.</p>
*
* <p>Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
* and j is row position. In fact, as the text says, i is row position and j is column position.</p>
*
* @author Sean Owen
*/
abstract class DataMask {
/**
* See ISO 18004:2006 6.8.1
*/
private static final DataMask[] DATA_MASKS = {
new DataMask000(),
new DataMask001(),
new DataMask010(),
new DataMask011(),
new DataMask100(),
new DataMask101(),
new DataMask110(),
new DataMask111(),
};
private DataMask() {
}
/**
* <p>Implementations of this method reverse the data masking process applied to a QR Code and
* make its bits ready to read.</p>
*
* @param bits representation of QR Code bits
* @param dimension dimension of QR Code, represented by bits, being unmasked
*/
final void unmaskBitMatrix(BitMatrix bits, int dimension) {
for (int i = 0; i < dimension; i++) {
for (int j = 0; j < dimension; j++) {
if (isMasked(i, j)) {
bits.flip(j, i);
}
}
}
}
abstract boolean isMasked(int i, int j);
/**
* @param reference a value between 0 and 7 indicating one of the eight possible
* data mask patterns a QR Code may use
* @return {@link DataMask} encapsulating the data mask pattern
*/
static DataMask forReference(int reference) {
if (reference < 0 || reference > 7) {
throw new IllegalArgumentException();
}
return DATA_MASKS[reference];
}
/**
* 000: mask bits for which (x + y) mod 2 == 0
*/
private static class DataMask000 extends DataMask {
boolean isMasked(int i, int j) {
return ((i + j) & 0x01) == 0;
}
}
/**
* 001: mask bits for which x mod 2 == 0
*/
private static class DataMask001 extends DataMask {
boolean isMasked(int i, int j) {
return (i & 0x01) == 0;
}
}
/**
* 010: mask bits for which y mod 3 == 0
*/
private static class DataMask010 extends DataMask {
boolean isMasked(int i, int j) {
return j % 3 == 0;
}
}
/**
* 011: mask bits for which (x + y) mod 3 == 0
*/
private static class DataMask011 extends DataMask {
boolean isMasked(int i, int j) {
return (i + j) % 3 == 0;
}
}
/**
* 100: mask bits for which (x/2 + y/3) mod 2 == 0
*/
private static class DataMask100 extends DataMask {
boolean isMasked(int i, int j) {
return (((i >>> 1) + (j /3)) & 0x01) == 0;
}
}
/**
* 101: mask bits for which xy mod 2 + xy mod 3 == 0
*/
private static class DataMask101 extends DataMask {
boolean isMasked(int i, int j) {
int temp = i * j;
return (temp & 0x01) + (temp % 3) == 0;
}
}
/**
* 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
*/
private static class DataMask110 extends DataMask {
boolean isMasked(int i, int j) {
int temp = i * j;
return (((temp & 0x01) + (temp % 3)) & 0x01) == 0;
}
}
/**
* 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
*/
private static class DataMask111 extends DataMask {
boolean isMasked(int i, int j) {
return ((((i + j) & 0x01) + ((i * j) % 3)) & 0x01) == 0;
}
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2007 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.qrcode.decoder;
/**
* <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
* defined by the QR code standard.</p>
*
* @author Sean Owen
*/
public final class ErrorCorrectionLevel {
// No, we can't use an enum here. J2ME doesn't support it.
/**
* L = ~7% correction
*/
public static final ErrorCorrectionLevel L = new ErrorCorrectionLevel(0, 0x01, "L");
/**
* M = ~15% correction
*/
public static final ErrorCorrectionLevel M = new ErrorCorrectionLevel(1, 0x00, "M");
/**
* Q = ~25% correction
*/
public static final ErrorCorrectionLevel Q = new ErrorCorrectionLevel(2, 0x03, "Q");
/**
* H = ~30% correction
*/
public static final ErrorCorrectionLevel H = new ErrorCorrectionLevel(3, 0x02, "H");
private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q};
private final int ordinal;
private final int bits;
private final String name;
private ErrorCorrectionLevel(int ordinal, int bits, String name) {
this.ordinal = ordinal;
this.bits = bits;
this.name = name;
}
public int ordinal() {
return ordinal;
}
public int getBits() {
return bits;
}
public String getName() {
return name;
}
public String toString() {
return name;
}
/**
* @param bits int containing the two bits encoding a QR Code's error correction level
* @return {@link ErrorCorrectionLevel} representing the encoded error correction level
*/
public static ErrorCorrectionLevel forBits(int bits) {
if (bits < 0 || bits >= FOR_BITS.length) {
throw new IllegalArgumentException();
}
return FOR_BITS[bits];
}
}

View File

@ -0,0 +1,171 @@
/*
* Copyright 2007 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.qrcode.decoder;
/**
* <p>Encapsulates a QR Code's format information, including the data mask used and
* error correction level.</p>
*
* @author Sean Owen
* @see DataMask
* @see ErrorCorrectionLevel
*/
final class FormatInformation {
private static final int FORMAT_INFO_MASK_QR = 0x5412;
/**
* See ISO 18004:2006, Annex C, Table C.1
*/
private static final int[][] FORMAT_INFO_DECODE_LOOKUP = {
{0x5412, 0x00},
{0x5125, 0x01},
{0x5E7C, 0x02},
{0x5B4B, 0x03},
{0x45F9, 0x04},
{0x40CE, 0x05},
{0x4F97, 0x06},
{0x4AA0, 0x07},
{0x77C4, 0x08},
{0x72F3, 0x09},
{0x7DAA, 0x0A},
{0x789D, 0x0B},
{0x662F, 0x0C},
{0x6318, 0x0D},
{0x6C41, 0x0E},
{0x6976, 0x0F},
{0x1689, 0x10},
{0x13BE, 0x11},
{0x1CE7, 0x12},
{0x19D0, 0x13},
{0x0762, 0x14},
{0x0255, 0x15},
{0x0D0C, 0x16},
{0x083B, 0x17},
{0x355F, 0x18},
{0x3068, 0x19},
{0x3F31, 0x1A},
{0x3A06, 0x1B},
{0x24B4, 0x1C},
{0x2183, 0x1D},
{0x2EDA, 0x1E},
{0x2BED, 0x1F},
};
/**
* Offset i holds the number of 1 bits in the binary representation of i
*/
private static final int[] BITS_SET_IN_HALF_BYTE =
{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
private final ErrorCorrectionLevel errorCorrectionLevel;
private final byte dataMask;
private FormatInformation(int formatInfo) {
// Bits 3,4
errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
// Bottom 3 bits
dataMask = (byte) (formatInfo & 0x07);
}
static int numBitsDiffering(int a, int b) {
a ^= b; // a now has a 1 bit exactly where its bit differs with b's
// Count bits set quickly with a series of lookups:
return BITS_SET_IN_HALF_BYTE[a & 0x0F] +
BITS_SET_IN_HALF_BYTE[(a >>> 4 & 0x0F)] +
BITS_SET_IN_HALF_BYTE[(a >>> 8 & 0x0F)] +
BITS_SET_IN_HALF_BYTE[(a >>> 12 & 0x0F)] +
BITS_SET_IN_HALF_BYTE[(a >>> 16 & 0x0F)] +
BITS_SET_IN_HALF_BYTE[(a >>> 20 & 0x0F)] +
BITS_SET_IN_HALF_BYTE[(a >>> 24 & 0x0F)] +
BITS_SET_IN_HALF_BYTE[(a >>> 28 & 0x0F)];
}
/**
* @param maskedFormatInfo1 format info indicator, with mask still applied
* @param maskedFormatInfo2 second copy of same info; both are checked at the same time
* to establish best match
* @return information about the format it specifies, or <code>null</code>
* if doesn't seem to match any known pattern
*/
static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
if (formatInfo != null) {
return formatInfo;
}
// Should return null, but, some QR codes apparently
// do not mask this info. Try again by actually masking the pattern
// first
return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR,
maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR);
}
private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
int bestDifference = Integer.MAX_VALUE;
int bestFormatInfo = 0;
for (int i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) {
int[] decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i];
int targetInfo = decodeInfo[0];
if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) {
// Found an exact match
return new FormatInformation(decodeInfo[1]);
}
int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo);
if (bitsDifference < bestDifference) {
bestFormatInfo = decodeInfo[1];
bestDifference = bitsDifference;
}
if (maskedFormatInfo1 != maskedFormatInfo2) {
// also try the other option
bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo);
if (bitsDifference < bestDifference) {
bestFormatInfo = decodeInfo[1];
bestDifference = bitsDifference;
}
}
}
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
// differing means we found a match
if (bestDifference <= 3) {
return new FormatInformation(bestFormatInfo);
}
return null;
}
ErrorCorrectionLevel getErrorCorrectionLevel() {
return errorCorrectionLevel;
}
byte getDataMask() {
return dataMask;
}
public int hashCode() {
return (errorCorrectionLevel.ordinal() << 3) | (int) dataMask;
}
public boolean equals(Object o) {
if (!(o instanceof FormatInformation)) {
return false;
}
FormatInformation other = (FormatInformation) o;
return this.errorCorrectionLevel == other.errorCorrectionLevel &&
this.dataMask == other.dataMask;
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright 2007 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.qrcode.decoder;
/**
* <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
* data can be encoded to bits in the QR code standard.</p>
*
* @author Sean Owen
*/
public final class Mode {
// No, we can't use an enum here. J2ME doesn't support it.
public static final Mode TERMINATOR = new Mode(new int[]{0, 0, 0}, 0x00, "TERMINATOR"); // Not really a mode...
public static final Mode NUMERIC = new Mode(new int[]{10, 12, 14}, 0x01, "NUMERIC");
public static final Mode ALPHANUMERIC = new Mode(new int[]{9, 11, 13}, 0x02, "ALPHANUMERIC");
public static final Mode STRUCTURED_APPEND = new Mode(new int[]{0, 0, 0}, 0x03, "STRUCTURED_APPEND"); // Not supported
public static final Mode BYTE = new Mode(new int[]{8, 16, 16}, 0x04, "BYTE");
public static final Mode ECI = new Mode(null, 0x07, "ECI"); // character counts don't apply
public static final Mode KANJI = new Mode(new int[]{8, 10, 12}, 0x08, "KANJI");
public static final Mode FNC1_FIRST_POSITION = new Mode(null, 0x05, "FNC1_FIRST_POSITION");
public static final Mode FNC1_SECOND_POSITION = new Mode(null, 0x09, "FNC1_SECOND_POSITION");
private final int[] characterCountBitsForVersions;
private final int bits;
private final String name;
private Mode(int[] characterCountBitsForVersions, int bits, String name) {
this.characterCountBitsForVersions = characterCountBitsForVersions;
this.bits = bits;
this.name = name;
}
/**
* @param bits four bits encoding a QR Code data mode
* @return {@link Mode} encoded by these bits
* @throws IllegalArgumentException if bits do not correspond to a known mode
*/
public static Mode forBits(int bits) {
switch (bits) {
case 0x0:
return TERMINATOR;
case 0x1:
return NUMERIC;
case 0x2:
return ALPHANUMERIC;
case 0x3:
return STRUCTURED_APPEND;
case 0x4:
return BYTE;
case 0x5:
return FNC1_FIRST_POSITION;
case 0x7:
return ECI;
case 0x8:
return KANJI;
case 0x9:
return FNC1_SECOND_POSITION;
default:
throw new IllegalArgumentException();
}
}
/**
* @param version version in question
* @return number of bits used, in this QR Code symbol {@link Version}, to encode the
* count of characters that will follow encoded in this {@link Mode}
*/
public int getCharacterCountBits(Version version) {
if (characterCountBitsForVersions == null) {
throw new IllegalArgumentException("Character count doesn't apply to this mode");
}
int number = version.getVersionNumber();
int offset;
if (number <= 9) {
offset = 0;
} else if (number <= 26) {
offset = 1;
} else {
offset = 2;
}
return characterCountBitsForVersions[offset];
}
public int getBits() {
return bits;
}
public String getName() {
return name;
}
public String toString() {
return name;
}
}

View File

@ -0,0 +1,586 @@
/*
* Copyright 2007 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.qrcode.decoder;
import com.google.zxing.FormatException;
import com.google.zxing.common.BitMatrix;
/**
* See ISO 18004:2006 Annex D
*
* @author Sean Owen
*/
public final class Version {
/**
* See ISO 18004:2006 Annex D.
* Element i represents the raw version bits that specify version i + 7
*/
private static final int[] VERSION_DECODE_INFO = {
0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6,
0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78,
0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683,
0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB,
0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250,
0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B,
0x2542E, 0x26A64, 0x27541, 0x28C69
};
private static final Version[] VERSIONS = buildVersions();
private final int versionNumber;
private final int[] alignmentPatternCenters;
private final ECBlocks[] ecBlocks;
private final int totalCodewords;
private Version(int versionNumber,
int[] alignmentPatternCenters,
ECBlocks ecBlocks1,
ECBlocks ecBlocks2,
ECBlocks ecBlocks3,
ECBlocks ecBlocks4) {
this.versionNumber = versionNumber;
this.alignmentPatternCenters = alignmentPatternCenters;
this.ecBlocks = new ECBlocks[]{ecBlocks1, ecBlocks2, ecBlocks3, ecBlocks4};
int total = 0;
int ecCodewords = ecBlocks1.getECCodewordsPerBlock();
ECB[] ecbArray = ecBlocks1.getECBlocks();
for (int i = 0; i < ecbArray.length; i++) {
ECB ecBlock = ecbArray[i];
total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords);
}
this.totalCodewords = total;
}
public int getVersionNumber() {
return versionNumber;
}
public int[] getAlignmentPatternCenters() {
return alignmentPatternCenters;
}
public int getTotalCodewords() {
return totalCodewords;
}
public int getDimensionForVersion() {
return 17 + 4 * versionNumber;
}
public ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel) {
return ecBlocks[ecLevel.ordinal()];
}
/**
* <p>Deduces version information purely from QR Code dimensions.</p>
*
* @param dimension dimension in modules
* @return {@link Version} for a QR Code of that dimension
* @throws FormatException if dimension is not 1 mod 4
*/
public static Version getProvisionalVersionForDimension(int dimension) throws FormatException {
if (dimension % 4 != 1) {
throw FormatException.getFormatInstance();
}
try {
return getVersionForNumber((dimension - 17) >> 2);
} catch (IllegalArgumentException iae) {
throw FormatException.getFormatInstance();
}
}
public static Version getVersionForNumber(int versionNumber) {
if (versionNumber < 1 || versionNumber > 40) {
throw new IllegalArgumentException();
}
return VERSIONS[versionNumber - 1];
}
static Version decodeVersionInformation(int versionBits) {
int bestDifference = Integer.MAX_VALUE;
int bestVersion = 0;
for (int i = 0; i < VERSION_DECODE_INFO.length; i++) {
int targetVersion = VERSION_DECODE_INFO[i];
// Do the version info bits match exactly? done.
if (targetVersion == versionBits) {
return getVersionForNumber(i + 7);
}
// Otherwise see if this is the closest to a real version info bit string
// we have seen so far
int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion);
if (bitsDifference < bestDifference) {
bestVersion = i + 7;
bestDifference = bitsDifference;
}
}
// We can tolerate up to 3 bits of error since no two version info codewords will
// differ in less than 8 bits.
if (bestDifference <= 3) {
return getVersionForNumber(bestVersion);
}
// If we didn't find a close enough match, fail
return null;
}
/**
* See ISO 18004:2006 Annex E
*/
BitMatrix buildFunctionPattern() {
int dimension = getDimensionForVersion();
BitMatrix bitMatrix = new BitMatrix(dimension);
// Top left finder pattern + separator + format
bitMatrix.setRegion(0, 0, 9, 9);
// Top right finder pattern + separator + format
bitMatrix.setRegion(dimension - 8, 0, 8, 9);
// Bottom left finder pattern + separator + format
bitMatrix.setRegion(0, dimension - 8, 9, 8);
// Alignment patterns
int max = alignmentPatternCenters.length;
for (int x = 0; x < max; x++) {
int i = alignmentPatternCenters[x] - 2;
for (int y = 0; y < max; y++) {
if ((x == 0 && (y == 0 || y == max - 1)) || (x == max - 1 && y == 0)) {
// No alignment patterns near the three finder paterns
continue;
}
bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5);
}
}
// Vertical timing pattern
bitMatrix.setRegion(6, 9, 1, dimension - 17);
// Horizontal timing pattern
bitMatrix.setRegion(9, 6, dimension - 17, 1);
if (versionNumber > 6) {
// Version info, top right
bitMatrix.setRegion(dimension - 11, 0, 3, 6);
// Version info, bottom left
bitMatrix.setRegion(0, dimension - 11, 6, 3);
}
return bitMatrix;
}
/**
* <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
* use blocks of differing sizes within one version, so, this encapsulates the parameters for
* each set of blocks. It also holds the number of error-correction codewords per block since it
* will be the same across all blocks within one version.</p>
*/
public static final class ECBlocks {
private final int ecCodewordsPerBlock;
private final ECB[] ecBlocks;
ECBlocks(int ecCodewordsPerBlock, ECB ecBlocks) {
this.ecCodewordsPerBlock = ecCodewordsPerBlock;
this.ecBlocks = new ECB[]{ecBlocks};
}
ECBlocks(int ecCodewordsPerBlock, ECB ecBlocks1, ECB ecBlocks2) {
this.ecCodewordsPerBlock = ecCodewordsPerBlock;
this.ecBlocks = new ECB[]{ecBlocks1, ecBlocks2};
}
public int getECCodewordsPerBlock() {
return ecCodewordsPerBlock;
}
public int getNumBlocks() {
int total = 0;
for (int i = 0; i < ecBlocks.length; i++) {
total += ecBlocks[i].getCount();
}
return total;
}
public int getTotalECCodewords() {
return ecCodewordsPerBlock * getNumBlocks();
}
public ECB[] getECBlocks() {
return ecBlocks;
}
}
/**
* <p>Encapsualtes the parameters for one error-correction block in one symbol version.
* This includes the number of data codewords, and the number of times a block with these
* parameters is used consecutively in the QR code version's format.</p>
*/
public static final class ECB {
private final int count;
private final int dataCodewords;
ECB(int count, int dataCodewords) {
this.count = count;
this.dataCodewords = dataCodewords;
}
public int getCount() {
return count;
}
public int getDataCodewords() {
return dataCodewords;
}
}
public String toString() {
return String.valueOf(versionNumber);
}
/**
* See ISO 18004:2006 6.5.1 Table 9
*/
private static Version[] buildVersions() {
return new Version[]{
new Version(1, new int[]{},
new ECBlocks(7, new ECB(1, 19)),
new ECBlocks(10, new ECB(1, 16)),
new ECBlocks(13, new ECB(1, 13)),
new ECBlocks(17, new ECB(1, 9))),
new Version(2, new int[]{6, 18},
new ECBlocks(10, new ECB(1, 34)),
new ECBlocks(16, new ECB(1, 28)),
new ECBlocks(22, new ECB(1, 22)),
new ECBlocks(28, new ECB(1, 16))),
new Version(3, new int[]{6, 22},
new ECBlocks(15, new ECB(1, 55)),
new ECBlocks(26, new ECB(1, 44)),
new ECBlocks(18, new ECB(2, 17)),
new ECBlocks(22, new ECB(2, 13))),
new Version(4, new int[]{6, 26},
new ECBlocks(20, new ECB(1, 80)),
new ECBlocks(18, new ECB(2, 32)),
new ECBlocks(26, new ECB(2, 24)),
new ECBlocks(16, new ECB(4, 9))),
new Version(5, new int[]{6, 30},
new ECBlocks(26, new ECB(1, 108)),
new ECBlocks(24, new ECB(2, 43)),
new ECBlocks(18, new ECB(2, 15),
new ECB(2, 16)),
new ECBlocks(22, new ECB(2, 11),
new ECB(2, 12))),
new Version(6, new int[]{6, 34},
new ECBlocks(18, new ECB(2, 68)),
new ECBlocks(16, new ECB(4, 27)),
new ECBlocks(24, new ECB(4, 19)),
new ECBlocks(28, new ECB(4, 15))),
new Version(7, new int[]{6, 22, 38},
new ECBlocks(20, new ECB(2, 78)),
new ECBlocks(18, new ECB(4, 31)),
new ECBlocks(18, new ECB(2, 14),
new ECB(4, 15)),
new ECBlocks(26, new ECB(4, 13),
new ECB(1, 14))),
new Version(8, new int[]{6, 24, 42},
new ECBlocks(24, new ECB(2, 97)),
new ECBlocks(22, new ECB(2, 38),
new ECB(2, 39)),
new ECBlocks(22, new ECB(4, 18),
new ECB(2, 19)),
new ECBlocks(26, new ECB(4, 14),
new ECB(2, 15))),
new Version(9, new int[]{6, 26, 46},
new ECBlocks(30, new ECB(2, 116)),
new ECBlocks(22, new ECB(3, 36),
new ECB(2, 37)),
new ECBlocks(20, new ECB(4, 16),
new ECB(4, 17)),
new ECBlocks(24, new ECB(4, 12),
new ECB(4, 13))),
new Version(10, new int[]{6, 28, 50},
new ECBlocks(18, new ECB(2, 68),
new ECB(2, 69)),
new ECBlocks(26, new ECB(4, 43),
new ECB(1, 44)),
new ECBlocks(24, new ECB(6, 19),
new ECB(2, 20)),
new ECBlocks(28, new ECB(6, 15),
new ECB(2, 16))),
new Version(11, new int[]{6, 30, 54},
new ECBlocks(20, new ECB(4, 81)),
new ECBlocks(30, new ECB(1, 50),
new ECB(4, 51)),
new ECBlocks(28, new ECB(4, 22),
new ECB(4, 23)),
new ECBlocks(24, new ECB(3, 12),
new ECB(8, 13))),
new Version(12, new int[]{6, 32, 58},
new ECBlocks(24, new ECB(2, 92),
new ECB(2, 93)),
new ECBlocks(22, new ECB(6, 36),
new ECB(2, 37)),
new ECBlocks(26, new ECB(4, 20),
new ECB(6, 21)),
new ECBlocks(28, new ECB(7, 14),
new ECB(4, 15))),
new Version(13, new int[]{6, 34, 62},
new ECBlocks(26, new ECB(4, 107)),
new ECBlocks(22, new ECB(8, 37),
new ECB(1, 38)),
new ECBlocks(24, new ECB(8, 20),
new ECB(4, 21)),
new ECBlocks(22, new ECB(12, 11),
new ECB(4, 12))),
new Version(14, new int[]{6, 26, 46, 66},
new ECBlocks(30, new ECB(3, 115),
new ECB(1, 116)),
new ECBlocks(24, new ECB(4, 40),
new ECB(5, 41)),
new ECBlocks(20, new ECB(11, 16),
new ECB(5, 17)),
new ECBlocks(24, new ECB(11, 12),
new ECB(5, 13))),
new Version(15, new int[]{6, 26, 48, 70},
new ECBlocks(22, new ECB(5, 87),
new ECB(1, 88)),
new ECBlocks(24, new ECB(5, 41),
new ECB(5, 42)),
new ECBlocks(30, new ECB(5, 24),
new ECB(7, 25)),
new ECBlocks(24, new ECB(11, 12),
new ECB(7, 13))),
new Version(16, new int[]{6, 26, 50, 74},
new ECBlocks(24, new ECB(5, 98),
new ECB(1, 99)),
new ECBlocks(28, new ECB(7, 45),
new ECB(3, 46)),
new ECBlocks(24, new ECB(15, 19),
new ECB(2, 20)),
new ECBlocks(30, new ECB(3, 15),
new ECB(13, 16))),
new Version(17, new int[]{6, 30, 54, 78},
new ECBlocks(28, new ECB(1, 107),
new ECB(5, 108)),
new ECBlocks(28, new ECB(10, 46),
new ECB(1, 47)),
new ECBlocks(28, new ECB(1, 22),
new ECB(15, 23)),
new ECBlocks(28, new ECB(2, 14),
new ECB(17, 15))),
new Version(18, new int[]{6, 30, 56, 82},
new ECBlocks(30, new ECB(5, 120),
new ECB(1, 121)),
new ECBlocks(26, new ECB(9, 43),
new ECB(4, 44)),
new ECBlocks(28, new ECB(17, 22),
new ECB(1, 23)),
new ECBlocks(28, new ECB(2, 14),
new ECB(19, 15))),
new Version(19, new int[]{6, 30, 58, 86},
new ECBlocks(28, new ECB(3, 113),
new ECB(4, 114)),
new ECBlocks(26, new ECB(3, 44),
new ECB(11, 45)),
new ECBlocks(26, new ECB(17, 21),
new ECB(4, 22)),
new ECBlocks(26, new ECB(9, 13),
new ECB(16, 14))),
new Version(20, new int[]{6, 34, 62, 90},
new ECBlocks(28, new ECB(3, 107),
new ECB(5, 108)),
new ECBlocks(26, new ECB(3, 41),
new ECB(13, 42)),
new ECBlocks(30, new ECB(15, 24),
new ECB(5, 25)),
new ECBlocks(28, new ECB(15, 15),
new ECB(10, 16))),
new Version(21, new int[]{6, 28, 50, 72, 94},
new ECBlocks(28, new ECB(4, 116),
new ECB(4, 117)),
new ECBlocks(26, new ECB(17, 42)),
new ECBlocks(28, new ECB(17, 22),
new ECB(6, 23)),
new ECBlocks(30, new ECB(19, 16),
new ECB(6, 17))),
new Version(22, new int[]{6, 26, 50, 74, 98},
new ECBlocks(28, new ECB(2, 111),
new ECB(7, 112)),
new ECBlocks(28, new ECB(17, 46)),
new ECBlocks(30, new ECB(7, 24),
new ECB(16, 25)),
new ECBlocks(24, new ECB(34, 13))),
new Version(23, new int[]{6, 30, 54, 78, 102},
new ECBlocks(30, new ECB(4, 121),
new ECB(5, 122)),
new ECBlocks(28, new ECB(4, 47),
new ECB(14, 48)),
new ECBlocks(30, new ECB(11, 24),
new ECB(14, 25)),
new ECBlocks(30, new ECB(16, 15),
new ECB(14, 16))),
new Version(24, new int[]{6, 28, 54, 80, 106},
new ECBlocks(30, new ECB(6, 117),
new ECB(4, 118)),
new ECBlocks(28, new ECB(6, 45),
new ECB(14, 46)),
new ECBlocks(30, new ECB(11, 24),
new ECB(16, 25)),
new ECBlocks(30, new ECB(30, 16),
new ECB(2, 17))),
new Version(25, new int[]{6, 32, 58, 84, 110},
new ECBlocks(26, new ECB(8, 106),
new ECB(4, 107)),
new ECBlocks(28, new ECB(8, 47),
new ECB(13, 48)),
new ECBlocks(30, new ECB(7, 24),
new ECB(22, 25)),
new ECBlocks(30, new ECB(22, 15),
new ECB(13, 16))),
new Version(26, new int[]{6, 30, 58, 86, 114},
new ECBlocks(28, new ECB(10, 114),
new ECB(2, 115)),
new ECBlocks(28, new ECB(19, 46),
new ECB(4, 47)),
new ECBlocks(28, new ECB(28, 22),
new ECB(6, 23)),
new ECBlocks(30, new ECB(33, 16),
new ECB(4, 17))),
new Version(27, new int[]{6, 34, 62, 90, 118},
new ECBlocks(30, new ECB(8, 122),
new ECB(4, 123)),
new ECBlocks(28, new ECB(22, 45),
new ECB(3, 46)),
new ECBlocks(30, new ECB(8, 23),
new ECB(26, 24)),
new ECBlocks(30, new ECB(12, 15),
new ECB(28, 16))),
new Version(28, new int[]{6, 26, 50, 74, 98, 122},
new ECBlocks(30, new ECB(3, 117),
new ECB(10, 118)),
new ECBlocks(28, new ECB(3, 45),
new ECB(23, 46)),
new ECBlocks(30, new ECB(4, 24),
new ECB(31, 25)),
new ECBlocks(30, new ECB(11, 15),
new ECB(31, 16))),
new Version(29, new int[]{6, 30, 54, 78, 102, 126},
new ECBlocks(30, new ECB(7, 116),
new ECB(7, 117)),
new ECBlocks(28, new ECB(21, 45),
new ECB(7, 46)),
new ECBlocks(30, new ECB(1, 23),
new ECB(37, 24)),
new ECBlocks(30, new ECB(19, 15),
new ECB(26, 16))),
new Version(30, new int[]{6, 26, 52, 78, 104, 130},
new ECBlocks(30, new ECB(5, 115),
new ECB(10, 116)),
new ECBlocks(28, new ECB(19, 47),
new ECB(10, 48)),
new ECBlocks(30, new ECB(15, 24),
new ECB(25, 25)),
new ECBlocks(30, new ECB(23, 15),
new ECB(25, 16))),
new Version(31, new int[]{6, 30, 56, 82, 108, 134},
new ECBlocks(30, new ECB(13, 115),
new ECB(3, 116)),
new ECBlocks(28, new ECB(2, 46),
new ECB(29, 47)),
new ECBlocks(30, new ECB(42, 24),
new ECB(1, 25)),
new ECBlocks(30, new ECB(23, 15),
new ECB(28, 16))),
new Version(32, new int[]{6, 34, 60, 86, 112, 138},
new ECBlocks(30, new ECB(17, 115)),
new ECBlocks(28, new ECB(10, 46),
new ECB(23, 47)),
new ECBlocks(30, new ECB(10, 24),
new ECB(35, 25)),
new ECBlocks(30, new ECB(19, 15),
new ECB(35, 16))),
new Version(33, new int[]{6, 30, 58, 86, 114, 142},
new ECBlocks(30, new ECB(17, 115),
new ECB(1, 116)),
new ECBlocks(28, new ECB(14, 46),
new ECB(21, 47)),
new ECBlocks(30, new ECB(29, 24),
new ECB(19, 25)),
new ECBlocks(30, new ECB(11, 15),
new ECB(46, 16))),
new Version(34, new int[]{6, 34, 62, 90, 118, 146},
new ECBlocks(30, new ECB(13, 115),
new ECB(6, 116)),
new ECBlocks(28, new ECB(14, 46),
new ECB(23, 47)),
new ECBlocks(30, new ECB(44, 24),
new ECB(7, 25)),
new ECBlocks(30, new ECB(59, 16),
new ECB(1, 17))),
new Version(35, new int[]{6, 30, 54, 78, 102, 126, 150},
new ECBlocks(30, new ECB(12, 121),
new ECB(7, 122)),
new ECBlocks(28, new ECB(12, 47),
new ECB(26, 48)),
new ECBlocks(30, new ECB(39, 24),
new ECB(14, 25)),
new ECBlocks(30, new ECB(22, 15),
new ECB(41, 16))),
new Version(36, new int[]{6, 24, 50, 76, 102, 128, 154},
new ECBlocks(30, new ECB(6, 121),
new ECB(14, 122)),
new ECBlocks(28, new ECB(6, 47),
new ECB(34, 48)),
new ECBlocks(30, new ECB(46, 24),
new ECB(10, 25)),
new ECBlocks(30, new ECB(2, 15),
new ECB(64, 16))),
new Version(37, new int[]{6, 28, 54, 80, 106, 132, 158},
new ECBlocks(30, new ECB(17, 122),
new ECB(4, 123)),
new ECBlocks(28, new ECB(29, 46),
new ECB(14, 47)),
new ECBlocks(30, new ECB(49, 24),
new ECB(10, 25)),
new ECBlocks(30, new ECB(24, 15),
new ECB(46, 16))),
new Version(38, new int[]{6, 32, 58, 84, 110, 136, 162},
new ECBlocks(30, new ECB(4, 122),
new ECB(18, 123)),
new ECBlocks(28, new ECB(13, 46),
new ECB(32, 47)),
new ECBlocks(30, new ECB(48, 24),
new ECB(14, 25)),
new ECBlocks(30, new ECB(42, 15),
new ECB(32, 16))),
new Version(39, new int[]{6, 26, 54, 82, 110, 138, 166},
new ECBlocks(30, new ECB(20, 117),
new ECB(4, 118)),
new ECBlocks(28, new ECB(40, 47),
new ECB(7, 48)),
new ECBlocks(30, new ECB(43, 24),
new ECB(22, 25)),
new ECBlocks(30, new ECB(10, 15),
new ECB(67, 16))),
new Version(40, new int[]{6, 30, 58, 86, 114, 142, 170},
new ECBlocks(30, new ECB(19, 118),
new ECB(6, 119)),
new ECBlocks(28, new ECB(18, 47),
new ECB(31, 48)),
new ECBlocks(30, new ECB(34, 24),
new ECB(34, 25)),
new ECBlocks(30, new ECB(20, 15),
new ECB(61, 16)))
};
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2008 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.qrcode.encoder;
final class BlockPair {
private final byte[] dataBytes;
private final byte[] errorCorrectionBytes;
BlockPair(byte[] data, byte[] errorCorrection) {
dataBytes = data;
errorCorrectionBytes = errorCorrection;
}
public byte[] getDataBytes() {
return dataBytes;
}
public byte[] getErrorCorrectionBytes() {
return errorCorrectionBytes;
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2008 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.qrcode.encoder;
/**
* A class which wraps a 2D array of bytes. The default usage is signed. If you want to use it as a
* unsigned container, it's up to you to do byteValue & 0xff at each location.
*
* JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned
* -1, 0, and 1, I'm going to use less memory and go with bytes.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class ByteMatrix {
private final byte[][] bytes;
private final int width;
private final int height;
public ByteMatrix(int width, int height) {
bytes = new byte[height][width];
this.width = width;
this.height = height;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public byte get(int x, int y) {
return bytes[y][x];
}
public byte[][] getArray() {
return bytes;
}
public void set(int x, int y, byte value) {
bytes[y][x] = value;
}
public void set(int x, int y, int value) {
bytes[y][x] = (byte) value;
}
public void set(int x, int y, boolean value) {
bytes[y][x] = (byte) (value ? 1 : 0);
}
public void clear(byte value) {
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
bytes[y][x] = value;
}
}
}
public String toString() {
StringBuffer result = new StringBuffer(2 * width * height + 2);
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
switch (bytes[y][x]) {
case 0:
result.append(" 0");
break;
case 1:
result.append(" 1");
break;
default:
result.append(" ");
break;
}
}
result.append('\n');
}
return result.toString();
}
}

View File

@ -0,0 +1,557 @@
/*
* Copyright 2008 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.qrcode.encoder;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;
import java.util.Vector;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.CharacterSetECI;
import com.google.zxing.common.ECI;
import com.google.zxing.common.reedsolomon.GF256;
import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Mode;
import com.google.zxing.qrcode.decoder.Version;
/**
* @author satorux@google.com (Satoru Takabayashi) - creator
* @author dswitkin@google.com (Daniel Switkin) - ported from C++
*/
public final class Encoder {
// The original table is defined in the table 5 of JISX0510:2004 (p.19).
private static final int[] ALPHANUMERIC_TABLE = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f
36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f
};
static final String DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1";
private Encoder() {
}
// The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details.
// Basically it applies four rules and summate all penalties.
private static int calculateMaskPenalty(ByteMatrix matrix) {
int penalty = 0;
penalty += MaskUtil.applyMaskPenaltyRule1(matrix);
penalty += MaskUtil.applyMaskPenaltyRule2(matrix);
penalty += MaskUtil.applyMaskPenaltyRule3(matrix);
penalty += MaskUtil.applyMaskPenaltyRule4(matrix);
return penalty;
}
/**
* Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen
* internally by chooseMode(). On success, store the result in "qrCode".
*
* We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for
* "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very
* strong error correction for this purpose.
*
* Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode()
* with which clients can specify the encoding mode. For now, we don't need the functionality.
*/
public static void encode(String content, ErrorCorrectionLevel ecLevel, QRCode qrCode)
throws WriterException {
encode(content, ecLevel, null, qrCode);
}
public static void encode(String content, ErrorCorrectionLevel ecLevel, Hashtable hints,
QRCode qrCode) throws WriterException {
String encoding = hints == null ? null : (String) hints.get(EncodeHintType.CHARACTER_SET);
if (encoding == null) {
encoding = DEFAULT_BYTE_MODE_ENCODING;
}
// Step 1: Choose the mode (encoding).
Mode mode = chooseMode(content, encoding);
// Step 2: Append "bytes" into "dataBits" in appropriate encoding.
BitArray dataBits = new BitArray();
appendBytes(content, mode, dataBits, encoding);
// Step 3: Initialize QR code that can contain "dataBits".
int numInputBytes = dataBits.getSizeInBytes();
initQRCode(numInputBytes, ecLevel, mode, qrCode);
// Step 4: Build another bit vector that contains header and data.
BitArray headerAndDataBits = new BitArray();
// Step 4.5: Append ECI message if applicable
if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.equals(encoding)) {
CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);
if (eci != null) {
appendECI(eci, headerAndDataBits);
}
}
appendModeInfo(mode, headerAndDataBits);
int numLetters = mode.equals(Mode.BYTE) ? dataBits.getSizeInBytes() : content.length();
appendLengthInfo(numLetters, qrCode.getVersion(), mode, headerAndDataBits);
headerAndDataBits.appendBitArray(dataBits);
// Step 5: Terminate the bits properly.
terminateBits(qrCode.getNumDataBytes(), headerAndDataBits);
// Step 6: Interleave data bits with error correction code.
BitArray finalBits = new BitArray();
interleaveWithECBytes(headerAndDataBits, qrCode.getNumTotalBytes(), qrCode.getNumDataBytes(),
qrCode.getNumRSBlocks(), finalBits);
// Step 7: Choose the mask pattern and set to "qrCode".
ByteMatrix matrix = new ByteMatrix(qrCode.getMatrixWidth(), qrCode.getMatrixWidth());
qrCode.setMaskPattern(chooseMaskPattern(finalBits, qrCode.getECLevel(), qrCode.getVersion(),
matrix));
// Step 8. Build the matrix and set it to "qrCode".
MatrixUtil.buildMatrix(finalBits, qrCode.getECLevel(), qrCode.getVersion(),
qrCode.getMaskPattern(), matrix);
qrCode.setMatrix(matrix);
// Step 9. Make sure we have a valid QR Code.
if (!qrCode.isValid()) {
throw new WriterException("Invalid QR code: " + qrCode.toString());
}
}
/**
* @return the code point of the table used in alphanumeric mode or
* -1 if there is no corresponding code in the table.
*/
static int getAlphanumericCode(int code) {
if (code < ALPHANUMERIC_TABLE.length) {
return ALPHANUMERIC_TABLE[code];
}
return -1;
}
public static Mode chooseMode(String content) {
return chooseMode(content, null);
}
/**
* Choose the best mode by examining the content. Note that 'encoding' is used as a hint;
* if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}.
*/
public static Mode chooseMode(String content, String encoding) {
if ("Shift_JIS".equals(encoding)) {
// Choose Kanji mode if all input are double-byte characters
return isOnlyDoubleByteKanji(content) ? Mode.KANJI : Mode.BYTE;
}
boolean hasNumeric = false;
boolean hasAlphanumeric = false;
for (int i = 0; i < content.length(); ++i) {
char c = content.charAt(i);
if (c >= '0' && c <= '9') {
hasNumeric = true;
} else if (getAlphanumericCode(c) != -1) {
hasAlphanumeric = true;
} else {
return Mode.BYTE;
}
}
if (hasAlphanumeric) {
return Mode.ALPHANUMERIC;
} else if (hasNumeric) {
return Mode.NUMERIC;
}
return Mode.BYTE;
}
private static boolean isOnlyDoubleByteKanji(String content) {
byte[] bytes;
try {
bytes = content.getBytes("Shift_JIS");
} catch (UnsupportedEncodingException uee) {
return false;
}
int length = bytes.length;
if (length % 2 != 0) {
return false;
}
for (int i = 0; i < length; i += 2) {
int byte1 = bytes[i] & 0xFF;
if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) {
return false;
}
}
return true;
}
private static int chooseMaskPattern(BitArray bits, ErrorCorrectionLevel ecLevel, int version,
ByteMatrix matrix) throws WriterException {
int minPenalty = Integer.MAX_VALUE; // Lower penalty is better.
int bestMaskPattern = -1;
// We try all mask patterns to choose the best one.
for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) {
MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix);
int penalty = calculateMaskPenalty(matrix);
if (penalty < minPenalty) {
minPenalty = penalty;
bestMaskPattern = maskPattern;
}
}
return bestMaskPattern;
}
/**
* Initialize "qrCode" according to "numInputBytes", "ecLevel", and "mode". On success,
* modify "qrCode".
*/
private static void initQRCode(int numInputBytes, ErrorCorrectionLevel ecLevel, Mode mode,
QRCode qrCode) throws WriterException {
qrCode.setECLevel(ecLevel);
qrCode.setMode(mode);
// In the following comments, we use numbers of Version 7-H.
for (int versionNum = 1; versionNum <= 40; versionNum++) {
Version version = Version.getVersionForNumber(versionNum);
// numBytes = 196
int numBytes = version.getTotalCodewords();
// getNumECBytes = 130
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
int numEcBytes = ecBlocks.getTotalECCodewords();
// getNumRSBlocks = 5
int numRSBlocks = ecBlocks.getNumBlocks();
// getNumDataBytes = 196 - 130 = 66
int numDataBytes = numBytes - numEcBytes;
// We want to choose the smallest version which can contain data of "numInputBytes" + some
// extra bits for the header (mode info and length info). The header can be three bytes
// (precisely 4 + 16 bits) at most. Hence we do +3 here.
if (numDataBytes >= numInputBytes + 3) {
// Yay, we found the proper rs block info!
qrCode.setVersion(versionNum);
qrCode.setNumTotalBytes(numBytes);
qrCode.setNumDataBytes(numDataBytes);
qrCode.setNumRSBlocks(numRSBlocks);
// getNumECBytes = 196 - 66 = 130
qrCode.setNumECBytes(numEcBytes);
// matrix width = 21 + 6 * 4 = 45
qrCode.setMatrixWidth(version.getDimensionForVersion());
return;
}
}
throw new WriterException("Cannot find proper rs block info (input data too big?)");
}
/**
* Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
*/
static void terminateBits(int numDataBytes, BitArray bits) throws WriterException {
int capacity = numDataBytes << 3;
if (bits.getSize() > capacity) {
throw new WriterException("data bits cannot fit in the QR Code" + bits.getSize() + " > " +
capacity);
}
for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) {
bits.appendBit(false);
}
// Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
// If the last byte isn't 8-bit aligned, we'll add padding bits.
int numBitsInLastByte = bits.getSize() & 0x07;
if (numBitsInLastByte > 0) {
for (int i = numBitsInLastByte; i < 8; i++) {
bits.appendBit(false);
}
}
// If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
int numPaddingBytes = numDataBytes - bits.getSizeInBytes();
for (int i = 0; i < numPaddingBytes; ++i) {
bits.appendBits(((i & 0x01) == 0) ? 0xEC : 0x11, 8);
}
if (bits.getSize() != capacity) {
throw new WriterException("Bits size does not equal capacity");
}
}
/**
* Get number of data bytes and number of error correction bytes for block id "blockID". Store
* the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of
* JISX0510:2004 (p.30)
*/
static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, int numDataBytes,
int numRSBlocks, int blockID, int[] numDataBytesInBlock,
int[] numECBytesInBlock) throws WriterException {
if (blockID >= numRSBlocks) {
throw new WriterException("Block ID too large");
}
// numRsBlocksInGroup2 = 196 % 5 = 1
int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks;
// numRsBlocksInGroup1 = 5 - 1 = 4
int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2;
// numTotalBytesInGroup1 = 196 / 5 = 39
int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks;
// numTotalBytesInGroup2 = 39 + 1 = 40
int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1;
// numDataBytesInGroup1 = 66 / 5 = 13
int numDataBytesInGroup1 = numDataBytes / numRSBlocks;
// numDataBytesInGroup2 = 13 + 1 = 14
int numDataBytesInGroup2 = numDataBytesInGroup1 + 1;
// numEcBytesInGroup1 = 39 - 13 = 26
int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1;
// numEcBytesInGroup2 = 40 - 14 = 26
int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2;
// Sanity checks.
// 26 = 26
if (numEcBytesInGroup1 != numEcBytesInGroup2) {
throw new WriterException("EC bytes mismatch");
}
// 5 = 4 + 1.
if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) {
throw new WriterException("RS blocks mismatch");
}
// 196 = (13 + 26) * 4 + (14 + 26) * 1
if (numTotalBytes !=
((numDataBytesInGroup1 + numEcBytesInGroup1) *
numRsBlocksInGroup1) +
((numDataBytesInGroup2 + numEcBytesInGroup2) *
numRsBlocksInGroup2)) {
throw new WriterException("Total bytes mismatch");
}
if (blockID < numRsBlocksInGroup1) {
numDataBytesInBlock[0] = numDataBytesInGroup1;
numECBytesInBlock[0] = numEcBytesInGroup1;
} else {
numDataBytesInBlock[0] = numDataBytesInGroup2;
numECBytesInBlock[0] = numEcBytesInGroup2;
}
}
/**
* Interleave "bits" with corresponding error correction bytes. On success, store the result in
* "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
*/
static void interleaveWithECBytes(BitArray bits, int numTotalBytes,
int numDataBytes, int numRSBlocks, BitArray result) throws WriterException {
// "bits" must have "getNumDataBytes" bytes of data.
if (bits.getSizeInBytes() != numDataBytes) {
throw new WriterException("Number of bits and data bytes does not match");
}
// Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll
// store the divided data bytes blocks and error correction bytes blocks into "blocks".
int dataBytesOffset = 0;
int maxNumDataBytes = 0;
int maxNumEcBytes = 0;
// Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.
Vector blocks = new Vector(numRSBlocks);
for (int i = 0; i < numRSBlocks; ++i) {
int[] numDataBytesInBlock = new int[1];
int[] numEcBytesInBlock = new int[1];
getNumDataBytesAndNumECBytesForBlockID(
numTotalBytes, numDataBytes, numRSBlocks, i,
numDataBytesInBlock, numEcBytesInBlock);
int size = numDataBytesInBlock[0];
byte[] dataBytes = new byte[size];
bits.toBytes(8*dataBytesOffset, dataBytes, 0, size);
byte[] ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]);
blocks.addElement(new BlockPair(dataBytes, ecBytes));
maxNumDataBytes = Math.max(maxNumDataBytes, size);
maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.length);
dataBytesOffset += numDataBytesInBlock[0];
}
if (numDataBytes != dataBytesOffset) {
throw new WriterException("Data bytes does not match offset");
}
// First, place data blocks.
for (int i = 0; i < maxNumDataBytes; ++i) {
for (int j = 0; j < blocks.size(); ++j) {
byte[] dataBytes = ((BlockPair) blocks.elementAt(j)).getDataBytes();
if (i < dataBytes.length) {
result.appendBits(dataBytes[i], 8);
}
}
}
// Then, place error correction blocks.
for (int i = 0; i < maxNumEcBytes; ++i) {
for (int j = 0; j < blocks.size(); ++j) {
byte[] ecBytes = ((BlockPair) blocks.elementAt(j)).getErrorCorrectionBytes();
if (i < ecBytes.length) {
result.appendBits(ecBytes[i], 8);
}
}
}
if (numTotalBytes != result.getSizeInBytes()) { // Should be same.
throw new WriterException("Interleaving error: " + numTotalBytes + " and " +
result.getSizeInBytes() + " differ.");
}
}
static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock) {
int numDataBytes = dataBytes.length;
int[] toEncode = new int[numDataBytes + numEcBytesInBlock];
for (int i = 0; i < numDataBytes; i++) {
toEncode[i] = dataBytes[i] & 0xFF;
}
new ReedSolomonEncoder(GF256.QR_CODE_FIELD).encode(toEncode, numEcBytesInBlock);
byte[] ecBytes = new byte[numEcBytesInBlock];
for (int i = 0; i < numEcBytesInBlock; i++) {
ecBytes[i] = (byte) toEncode[numDataBytes + i];
}
return ecBytes;
}
/**
* Append mode info. On success, store the result in "bits".
*/
static void appendModeInfo(Mode mode, BitArray bits) {
bits.appendBits(mode.getBits(), 4);
}
/**
* Append length info. On success, store the result in "bits".
*/
static void appendLengthInfo(int numLetters, int version, Mode mode, BitArray bits)
throws WriterException {
int numBits = mode.getCharacterCountBits(Version.getVersionForNumber(version));
if (numLetters > ((1 << numBits) - 1)) {
throw new WriterException(numLetters + "is bigger than" + ((1 << numBits) - 1));
}
bits.appendBits(numLetters, numBits);
}
/**
* Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
*/
static void appendBytes(String content, Mode mode, BitArray bits, String encoding)
throws WriterException {
if (mode.equals(Mode.NUMERIC)) {
appendNumericBytes(content, bits);
} else if (mode.equals(Mode.ALPHANUMERIC)) {
appendAlphanumericBytes(content, bits);
} else if (mode.equals(Mode.BYTE)) {
append8BitBytes(content, bits, encoding);
} else if (mode.equals(Mode.KANJI)) {
appendKanjiBytes(content, bits);
} else {
throw new WriterException("Invalid mode: " + mode);
}
}
static void appendNumericBytes(String content, BitArray bits) {
int length = content.length();
int i = 0;
while (i < length) {
int num1 = content.charAt(i) - '0';
if (i + 2 < length) {
// Encode three numeric letters in ten bits.
int num2 = content.charAt(i + 1) - '0';
int num3 = content.charAt(i + 2) - '0';
bits.appendBits(num1 * 100 + num2 * 10 + num3, 10);
i += 3;
} else if (i + 1 < length) {
// Encode two numeric letters in seven bits.
int num2 = content.charAt(i + 1) - '0';
bits.appendBits(num1 * 10 + num2, 7);
i += 2;
} else {
// Encode one numeric letter in four bits.
bits.appendBits(num1, 4);
i++;
}
}
}
static void appendAlphanumericBytes(String content, BitArray bits) throws WriterException {
int length = content.length();
int i = 0;
while (i < length) {
int code1 = getAlphanumericCode(content.charAt(i));
if (code1 == -1) {
throw new WriterException();
}
if (i + 1 < length) {
int code2 = getAlphanumericCode(content.charAt(i + 1));
if (code2 == -1) {
throw new WriterException();
}
// Encode two alphanumeric letters in 11 bits.
bits.appendBits(code1 * 45 + code2, 11);
i += 2;
} else {
// Encode one alphanumeric letter in six bits.
bits.appendBits(code1, 6);
i++;
}
}
}
static void append8BitBytes(String content, BitArray bits, String encoding)
throws WriterException {
byte[] bytes;
try {
bytes = content.getBytes(encoding);
} catch (UnsupportedEncodingException uee) {
throw new WriterException(uee.toString());
}
for (int i = 0; i < bytes.length; ++i) {
bits.appendBits(bytes[i], 8);
}
}
static void appendKanjiBytes(String content, BitArray bits) throws WriterException {
byte[] bytes;
try {
bytes = content.getBytes("Shift_JIS");
} catch (UnsupportedEncodingException uee) {
throw new WriterException(uee.toString());
}
int length = bytes.length;
for (int i = 0; i < length; i += 2) {
int byte1 = bytes[i] & 0xFF;
int byte2 = bytes[i + 1] & 0xFF;
int code = (byte1 << 8) | byte2;
int subtracted = -1;
if (code >= 0x8140 && code <= 0x9ffc) {
subtracted = code - 0x8140;
} else if (code >= 0xe040 && code <= 0xebbf) {
subtracted = code - 0xc140;
}
if (subtracted == -1) {
throw new WriterException("Invalid byte sequence");
}
int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
bits.appendBits(encoded, 13);
}
}
private static void appendECI(ECI eci, BitArray bits) {
bits.appendBits(Mode.ECI.getBits(), 4);
// This is correct for values up to 127, which is all we need now.
bits.appendBits(eci.getValue(), 8);
}
}

View File

@ -0,0 +1,217 @@
/*
* Copyright 2008 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.qrcode.encoder;
/**
* @author satorux@google.com (Satoru Takabayashi) - creator
* @author dswitkin@google.com (Daniel Switkin) - ported from C++
*/
public final class MaskUtil {
private MaskUtil() {
// do nothing
}
// Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and
// give penalty to them. Example: 00000 or 11111.
public static int applyMaskPenaltyRule1(ByteMatrix matrix) {
return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false);
}
// Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give
// penalty to them.
public static int applyMaskPenaltyRule2(ByteMatrix matrix) {
int penalty = 0;
byte[][] array = matrix.getArray();
int width = matrix.getWidth();
int height = matrix.getHeight();
for (int y = 0; y < height - 1; ++y) {
for (int x = 0; x < width - 1; ++x) {
int value = array[y][x];
if (value == array[y][x + 1] && value == array[y + 1][x] && value == array[y + 1][x + 1]) {
penalty += 3;
}
}
}
return penalty;
}
// Apply mask penalty rule 3 and return the penalty. Find consecutive cells of 00001011101 or
// 10111010000, and give penalty to them. If we find patterns like 000010111010000, we give
// penalties twice (i.e. 40 * 2).
public static int applyMaskPenaltyRule3(ByteMatrix matrix) {
int penalty = 0;
byte[][] array = matrix.getArray();
int width = matrix.getWidth();
int height = matrix.getHeight();
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
// Tried to simplify following conditions but failed.
if (x + 6 < width &&
array[y][x] == 1 &&
array[y][x + 1] == 0 &&
array[y][x + 2] == 1 &&
array[y][x + 3] == 1 &&
array[y][x + 4] == 1 &&
array[y][x + 5] == 0 &&
array[y][x + 6] == 1 &&
((x + 10 < width &&
array[y][x + 7] == 0 &&
array[y][x + 8] == 0 &&
array[y][x + 9] == 0 &&
array[y][x + 10] == 0) ||
(x - 4 >= 0 &&
array[y][x - 1] == 0 &&
array[y][x - 2] == 0 &&
array[y][x - 3] == 0 &&
array[y][x - 4] == 0))) {
penalty += 40;
}
if (y + 6 < height &&
array[y][x] == 1 &&
array[y + 1][x] == 0 &&
array[y + 2][x] == 1 &&
array[y + 3][x] == 1 &&
array[y + 4][x] == 1 &&
array[y + 5][x] == 0 &&
array[y + 6][x] == 1 &&
((y + 10 < height &&
array[y + 7][x] == 0 &&
array[y + 8][x] == 0 &&
array[y + 9][x] == 0 &&
array[y + 10][x] == 0) ||
(y - 4 >= 0 &&
array[y - 1][x] == 0 &&
array[y - 2][x] == 0 &&
array[y - 3][x] == 0 &&
array[y - 4][x] == 0))) {
penalty += 40;
}
}
}
return penalty;
}
// Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give
// penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance. Examples:
// - 0% => 100
// - 40% => 20
// - 45% => 10
// - 50% => 0
// - 55% => 10
// - 55% => 20
// - 100% => 100
public static int applyMaskPenaltyRule4(ByteMatrix matrix) {
int numDarkCells = 0;
byte[][] array = matrix.getArray();
int width = matrix.getWidth();
int height = matrix.getHeight();
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
if (array[y][x] == 1) {
numDarkCells += 1;
}
}
}
int numTotalCells = matrix.getHeight() * matrix.getWidth();
double darkRatio = (double) numDarkCells / numTotalCells;
return Math.abs((int) (darkRatio * 100 - 50)) / 5 * 10;
}
// Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask
// pattern conditions.
public static boolean getDataMaskBit(int maskPattern, int x, int y) {
if (!QRCode.isValidMaskPattern(maskPattern)) {
throw new IllegalArgumentException("Invalid mask pattern");
}
int intermediate, temp;
switch (maskPattern) {
case 0:
intermediate = (y + x) & 0x1;
break;
case 1:
intermediate = y & 0x1;
break;
case 2:
intermediate = x % 3;
break;
case 3:
intermediate = (y + x) % 3;
break;
case 4:
intermediate = ((y >>> 1) + (x / 3)) & 0x1;
break;
case 5:
temp = y * x;
intermediate = (temp & 0x1) + (temp % 3);
break;
case 6:
temp = y * x;
intermediate = (((temp & 0x1) + (temp % 3)) & 0x1);
break;
case 7:
temp = y * x;
intermediate = (((temp % 3) + ((y + x) & 0x1)) & 0x1);
break;
default:
throw new IllegalArgumentException("Invalid mask pattern: " + maskPattern);
}
return intermediate == 0;
}
// Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both
// vertical and horizontal orders respectively.
private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, boolean isHorizontal) {
int penalty = 0;
int numSameBitCells = 0;
int prevBit = -1;
// Horizontal mode:
// for (int i = 0; i < matrix.height(); ++i) {
// for (int j = 0; j < matrix.width(); ++j) {
// int bit = matrix.get(i, j);
// Vertical mode:
// for (int i = 0; i < matrix.width(); ++i) {
// for (int j = 0; j < matrix.height(); ++j) {
// int bit = matrix.get(j, i);
int iLimit = isHorizontal ? matrix.getHeight() : matrix.getWidth();
int jLimit = isHorizontal ? matrix.getWidth() : matrix.getHeight();
byte[][] array = matrix.getArray();
for (int i = 0; i < iLimit; ++i) {
for (int j = 0; j < jLimit; ++j) {
int bit = isHorizontal ? array[i][j] : array[j][i];
if (bit == prevBit) {
numSameBitCells += 1;
// Found five repetitive cells with the same color (bit).
// We'll give penalty of 3.
if (numSameBitCells == 5) {
penalty += 3;
} else if (numSameBitCells > 5) {
// After five repetitive cells, we'll add the penalty one
// by one.
penalty += 1;
}
} else {
numSameBitCells = 1; // Include the cell itself.
prevBit = bit;
}
}
numSameBitCells = 0; // Clear at each row/column.
}
return penalty;
}
}

View File

@ -0,0 +1,524 @@
/*
* Copyright 2008 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.qrcode.encoder;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitArray;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
/**
* @author satorux@google.com (Satoru Takabayashi) - creator
* @author dswitkin@google.com (Daniel Switkin) - ported from C++
*/
public final class MatrixUtil {
private MatrixUtil() {
// do nothing
}
private static final int[][] POSITION_DETECTION_PATTERN = {
{1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 1},
{1, 0, 1, 1, 1, 0, 1},
{1, 0, 1, 1, 1, 0, 1},
{1, 0, 1, 1, 1, 0, 1},
{1, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1},
};
private static final int[][] HORIZONTAL_SEPARATION_PATTERN = {
{0, 0, 0, 0, 0, 0, 0, 0},
};
private static final int[][] VERTICAL_SEPARATION_PATTERN = {
{0}, {0}, {0}, {0}, {0}, {0}, {0},
};
private static final int[][] POSITION_ADJUSTMENT_PATTERN = {
{1, 1, 1, 1, 1},
{1, 0, 0, 0, 1},
{1, 0, 1, 0, 1},
{1, 0, 0, 0, 1},
{1, 1, 1, 1, 1},
};
// From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu.
private static final int[][] POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = {
{-1, -1, -1, -1, -1, -1, -1}, // Version 1
{ 6, 18, -1, -1, -1, -1, -1}, // Version 2
{ 6, 22, -1, -1, -1, -1, -1}, // Version 3
{ 6, 26, -1, -1, -1, -1, -1}, // Version 4
{ 6, 30, -1, -1, -1, -1, -1}, // Version 5
{ 6, 34, -1, -1, -1, -1, -1}, // Version 6
{ 6, 22, 38, -1, -1, -1, -1}, // Version 7
{ 6, 24, 42, -1, -1, -1, -1}, // Version 8
{ 6, 26, 46, -1, -1, -1, -1}, // Version 9
{ 6, 28, 50, -1, -1, -1, -1}, // Version 10
{ 6, 30, 54, -1, -1, -1, -1}, // Version 11
{ 6, 32, 58, -1, -1, -1, -1}, // Version 12
{ 6, 34, 62, -1, -1, -1, -1}, // Version 13
{ 6, 26, 46, 66, -1, -1, -1}, // Version 14
{ 6, 26, 48, 70, -1, -1, -1}, // Version 15
{ 6, 26, 50, 74, -1, -1, -1}, // Version 16
{ 6, 30, 54, 78, -1, -1, -1}, // Version 17
{ 6, 30, 56, 82, -1, -1, -1}, // Version 18
{ 6, 30, 58, 86, -1, -1, -1}, // Version 19
{ 6, 34, 62, 90, -1, -1, -1}, // Version 20
{ 6, 28, 50, 72, 94, -1, -1}, // Version 21
{ 6, 26, 50, 74, 98, -1, -1}, // Version 22
{ 6, 30, 54, 78, 102, -1, -1}, // Version 23
{ 6, 28, 54, 80, 106, -1, -1}, // Version 24
{ 6, 32, 58, 84, 110, -1, -1}, // Version 25
{ 6, 30, 58, 86, 114, -1, -1}, // Version 26
{ 6, 34, 62, 90, 118, -1, -1}, // Version 27
{ 6, 26, 50, 74, 98, 122, -1}, // Version 28
{ 6, 30, 54, 78, 102, 126, -1}, // Version 29
{ 6, 26, 52, 78, 104, 130, -1}, // Version 30
{ 6, 30, 56, 82, 108, 134, -1}, // Version 31
{ 6, 34, 60, 86, 112, 138, -1}, // Version 32
{ 6, 30, 58, 86, 114, 142, -1}, // Version 33
{ 6, 34, 62, 90, 118, 146, -1}, // Version 34
{ 6, 30, 54, 78, 102, 126, 150}, // Version 35
{ 6, 24, 50, 76, 102, 128, 154}, // Version 36
{ 6, 28, 54, 80, 106, 132, 158}, // Version 37
{ 6, 32, 58, 84, 110, 136, 162}, // Version 38
{ 6, 26, 54, 82, 110, 138, 166}, // Version 39
{ 6, 30, 58, 86, 114, 142, 170}, // Version 40
};
// Type info cells at the left top corner.
private static final int[][] TYPE_INFO_COORDINATES = {
{8, 0},
{8, 1},
{8, 2},
{8, 3},
{8, 4},
{8, 5},
{8, 7},
{8, 8},
{7, 8},
{5, 8},
{4, 8},
{3, 8},
{2, 8},
{1, 8},
{0, 8},
};
// From Appendix D in JISX0510:2004 (p. 67)
private static final int VERSION_INFO_POLY = 0x1f25; // 1 1111 0010 0101
// From Appendix C in JISX0510:2004 (p.65).
private static final int TYPE_INFO_POLY = 0x537;
private static final int TYPE_INFO_MASK_PATTERN = 0x5412;
// Set all cells to -1. -1 means that the cell is empty (not set yet).
//
// JAVAPORT: We shouldn't need to do this at all. The code should be rewritten to begin encoding
// with the ByteMatrix initialized all to zero.
public static void clearMatrix(ByteMatrix matrix) {
matrix.clear((byte) -1);
}
// Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On
// success, store the result in "matrix" and return true.
public static void buildMatrix(BitArray dataBits, ErrorCorrectionLevel ecLevel, int version,
int maskPattern, ByteMatrix matrix) throws WriterException {
clearMatrix(matrix);
embedBasicPatterns(version, matrix);
// Type information appear with any version.
embedTypeInfo(ecLevel, maskPattern, matrix);
// Version info appear if version >= 7.
maybeEmbedVersionInfo(version, matrix);
// Data should be embedded at end.
embedDataBits(dataBits, maskPattern, matrix);
}
// Embed basic patterns. On success, modify the matrix and return true.
// The basic patterns are:
// - Position detection patterns
// - Timing patterns
// - Dark dot at the left bottom corner
// - Position adjustment patterns, if need be
public static void embedBasicPatterns(int version, ByteMatrix matrix) throws WriterException {
// Let's get started with embedding big squares at corners.
embedPositionDetectionPatternsAndSeparators(matrix);
// Then, embed the dark dot at the left bottom corner.
embedDarkDotAtLeftBottomCorner(matrix);
// Position adjustment patterns appear if version >= 2.
maybeEmbedPositionAdjustmentPatterns(version, matrix);
// Timing patterns should be embedded after position adj. patterns.
embedTimingPatterns(matrix);
}
// Embed type information. On success, modify the matrix.
public static void embedTypeInfo(ErrorCorrectionLevel ecLevel, int maskPattern, ByteMatrix matrix)
throws WriterException {
BitArray typeInfoBits = new BitArray();
makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits);
for (int i = 0; i < typeInfoBits.getSize(); ++i) {
// Place bits in LSB to MSB order. LSB (least significant bit) is the last value in
// "typeInfoBits".
boolean bit = typeInfoBits.get(typeInfoBits.getSize() - 1 - i);
// Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46).
int x1 = TYPE_INFO_COORDINATES[i][0];
int y1 = TYPE_INFO_COORDINATES[i][1];
matrix.set(x1, y1, bit);
if (i < 8) {
// Right top corner.
int x2 = matrix.getWidth() - i - 1;
int y2 = 8;
matrix.set(x2, y2, bit);
} else {
// Left bottom corner.
int x2 = 8;
int y2 = matrix.getHeight() - 7 + (i - 8);
matrix.set(x2, y2, bit);
}
}
}
// Embed version information if need be. On success, modify the matrix and return true.
// See 8.10 of JISX0510:2004 (p.47) for how to embed version information.
public static void maybeEmbedVersionInfo(int version, ByteMatrix matrix) throws WriterException {
if (version < 7) { // Version info is necessary if version >= 7.
return; // Don't need version info.
}
BitArray versionInfoBits = new BitArray();
makeVersionInfoBits(version, versionInfoBits);
int bitIndex = 6 * 3 - 1; // It will decrease from 17 to 0.
for (int i = 0; i < 6; ++i) {
for (int j = 0; j < 3; ++j) {
// Place bits in LSB (least significant bit) to MSB order.
boolean bit = versionInfoBits.get(bitIndex);
bitIndex--;
// Left bottom corner.
matrix.set(i, matrix.getHeight() - 11 + j, bit);
// Right bottom corner.
matrix.set(matrix.getHeight() - 11 + j, i, bit);
}
}
}
// Embed "dataBits" using "getMaskPattern". On success, modify the matrix and return true.
// For debugging purposes, it skips masking process if "getMaskPattern" is -1.
// See 8.7 of JISX0510:2004 (p.38) for how to embed data bits.
public static void embedDataBits(BitArray dataBits, int maskPattern, ByteMatrix matrix)
throws WriterException {
int bitIndex = 0;
int direction = -1;
// Start from the right bottom cell.
int x = matrix.getWidth() - 1;
int y = matrix.getHeight() - 1;
while (x > 0) {
// Skip the vertical timing pattern.
if (x == 6) {
x -= 1;
}
while (y >= 0 && y < matrix.getHeight()) {
for (int i = 0; i < 2; ++i) {
int xx = x - i;
// Skip the cell if it's not empty.
if (!isEmpty(matrix.get(xx, y))) {
continue;
}
boolean bit;
if (bitIndex < dataBits.getSize()) {
bit = dataBits.get(bitIndex);
++bitIndex;
} else {
// Padding bit. If there is no bit left, we'll fill the left cells with 0, as described
// in 8.4.9 of JISX0510:2004 (p. 24).
bit = false;
}
// Skip masking if mask_pattern is -1.
if (maskPattern != -1) {
if (MaskUtil.getDataMaskBit(maskPattern, xx, y)) {
bit = !bit;
}
}
matrix.set(xx, y, bit);
}
y += direction;
}
direction = -direction; // Reverse the direction.
y += direction;
x -= 2; // Move to the left.
}
// All bits should be consumed.
if (bitIndex != dataBits.getSize()) {
throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.getSize());
}
}
// Return the position of the most significant bit set (to one) in the "value". The most
// significant bit is position 32. If there is no bit set, return 0. Examples:
// - findMSBSet(0) => 0
// - findMSBSet(1) => 1
// - findMSBSet(255) => 8
public static int findMSBSet(int value) {
int numDigits = 0;
while (value != 0) {
value >>>= 1;
++numDigits;
}
return numDigits;
}
// Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH
// code is used for encoding type information and version information.
// Example: Calculation of version information of 7.
// f(x) is created from 7.
// - 7 = 000111 in 6 bits
// - f(x) = x^2 + x^1 + x^0
// g(x) is given by the standard (p. 67)
// - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1
// Multiply f(x) by x^(18 - 6)
// - f'(x) = f(x) * x^(18 - 6)
// - f'(x) = x^14 + x^13 + x^12
// Calculate the remainder of f'(x) / g(x)
// x^2
// __________________________________________________
// g(x) )x^14 + x^13 + x^12
// x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2
// --------------------------------------------------
// x^11 + x^10 + x^7 + x^4 + x^2
//
// The remainder is x^11 + x^10 + x^7 + x^4 + x^2
// Encode it in binary: 110010010100
// The return value is 0xc94 (1100 1001 0100)
//
// Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit
// operations. We don't care if cofficients are positive or negative.
public static int calculateBCHCode(int value, int poly) {
// If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1
// from 13 to make it 12.
int msbSetInPoly = findMSBSet(poly);
value <<= msbSetInPoly - 1;
// Do the division business using exclusive-or operations.
while (findMSBSet(value) >= msbSetInPoly) {
value ^= poly << (findMSBSet(value) - msbSetInPoly);
}
// Now the "value" is the remainder (i.e. the BCH code)
return value;
}
// Make bit vector of type information. On success, store the result in "bits" and return true.
// Encode error correction level and mask pattern. See 8.9 of
// JISX0510:2004 (p.45) for details.
public static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitArray bits)
throws WriterException {
if (!QRCode.isValidMaskPattern(maskPattern)) {
throw new WriterException("Invalid mask pattern");
}
int typeInfo = (ecLevel.getBits() << 3) | maskPattern;
bits.appendBits(typeInfo, 5);
int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY);
bits.appendBits(bchCode, 10);
BitArray maskBits = new BitArray();
maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15);
bits.xor(maskBits);
if (bits.getSize() != 15) { // Just in case.
throw new WriterException("should not happen but we got: " + bits.getSize());
}
}
// Make bit vector of version information. On success, store the result in "bits" and return true.
// See 8.10 of JISX0510:2004 (p.45) for details.
public static void makeVersionInfoBits(int version, BitArray bits) throws WriterException {
bits.appendBits(version, 6);
int bchCode = calculateBCHCode(version, VERSION_INFO_POLY);
bits.appendBits(bchCode, 12);
if (bits.getSize() != 18) { // Just in case.
throw new WriterException("should not happen but we got: " + bits.getSize());
}
}
// Check if "value" is empty.
private static boolean isEmpty(int value) {
return value == -1;
}
// Check if "value" is valid.
private static boolean isValidValue(int value) {
return (value == -1 || // Empty.
value == 0 || // Light (white).
value == 1); // Dark (black).
}
private static void embedTimingPatterns(ByteMatrix matrix) throws WriterException {
// -8 is for skipping position detection patterns (size 7), and two horizontal/vertical
// separation patterns (size 1). Thus, 8 = 7 + 1.
for (int i = 8; i < matrix.getWidth() - 8; ++i) {
int bit = (i + 1) % 2;
// Horizontal line.
if (!isValidValue(matrix.get(i, 6))) {
throw new WriterException();
}
if (isEmpty(matrix.get(i, 6))) {
matrix.set(i, 6, bit);
}
// Vertical line.
if (!isValidValue(matrix.get(6, i))) {
throw new WriterException();
}
if (isEmpty(matrix.get(6, i))) {
matrix.set(6, i, bit);
}
}
}
// Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46)
private static void embedDarkDotAtLeftBottomCorner(ByteMatrix matrix) throws WriterException {
if (matrix.get(8, matrix.getHeight() - 8) == 0) {
throw new WriterException();
}
matrix.set(8, matrix.getHeight() - 8, 1);
}
private static void embedHorizontalSeparationPattern(int xStart, int yStart,
ByteMatrix matrix) throws WriterException {
// We know the width and height.
if (HORIZONTAL_SEPARATION_PATTERN[0].length != 8 || HORIZONTAL_SEPARATION_PATTERN.length != 1) {
throw new WriterException("Bad horizontal separation pattern");
}
for (int x = 0; x < 8; ++x) {
if (!isEmpty(matrix.get(xStart + x, yStart))) {
throw new WriterException();
}
matrix.set(xStart + x, yStart, HORIZONTAL_SEPARATION_PATTERN[0][x]);
}
}
private static void embedVerticalSeparationPattern(int xStart, int yStart,
ByteMatrix matrix) throws WriterException {
// We know the width and height.
if (VERTICAL_SEPARATION_PATTERN[0].length != 1 || VERTICAL_SEPARATION_PATTERN.length != 7) {
throw new WriterException("Bad vertical separation pattern");
}
for (int y = 0; y < 7; ++y) {
if (!isEmpty(matrix.get(xStart, yStart + y))) {
throw new WriterException();
}
matrix.set(xStart, yStart + y, VERTICAL_SEPARATION_PATTERN[y][0]);
}
}
// Note that we cannot unify the function with embedPositionDetectionPattern() despite they are
// almost identical, since we cannot write a function that takes 2D arrays in different sizes in
// C/C++. We should live with the fact.
private static void embedPositionAdjustmentPattern(int xStart, int yStart,
ByteMatrix matrix) throws WriterException {
// We know the width and height.
if (POSITION_ADJUSTMENT_PATTERN[0].length != 5 || POSITION_ADJUSTMENT_PATTERN.length != 5) {
throw new WriterException("Bad position adjustment");
}
for (int y = 0; y < 5; ++y) {
for (int x = 0; x < 5; ++x) {
if (!isEmpty(matrix.get(xStart + x, yStart + y))) {
throw new WriterException();
}
matrix.set(xStart + x, yStart + y, POSITION_ADJUSTMENT_PATTERN[y][x]);
}
}
}
private static void embedPositionDetectionPattern(int xStart, int yStart,
ByteMatrix matrix) throws WriterException {
// We know the width and height.
if (POSITION_DETECTION_PATTERN[0].length != 7 || POSITION_DETECTION_PATTERN.length != 7) {
throw new WriterException("Bad position detection pattern");
}
for (int y = 0; y < 7; ++y) {
for (int x = 0; x < 7; ++x) {
if (!isEmpty(matrix.get(xStart + x, yStart + y))) {
throw new WriterException();
}
matrix.set(xStart + x, yStart + y, POSITION_DETECTION_PATTERN[y][x]);
}
}
}
// Embed position detection patterns and surrounding vertical/horizontal separators.
private static void embedPositionDetectionPatternsAndSeparators(ByteMatrix matrix) throws WriterException {
// Embed three big squares at corners.
int pdpWidth = POSITION_DETECTION_PATTERN[0].length;
// Left top corner.
embedPositionDetectionPattern(0, 0, matrix);
// Right top corner.
embedPositionDetectionPattern(matrix.getWidth() - pdpWidth, 0, matrix);
// Left bottom corner.
embedPositionDetectionPattern(0, matrix.getWidth() - pdpWidth, matrix);
// Embed horizontal separation patterns around the squares.
int hspWidth = HORIZONTAL_SEPARATION_PATTERN[0].length;
// Left top corner.
embedHorizontalSeparationPattern(0, hspWidth - 1, matrix);
// Right top corner.
embedHorizontalSeparationPattern(matrix.getWidth() - hspWidth,
hspWidth - 1, matrix);
// Left bottom corner.
embedHorizontalSeparationPattern(0, matrix.getWidth() - hspWidth, matrix);
// Embed vertical separation patterns around the squares.
int vspSize = VERTICAL_SEPARATION_PATTERN.length;
// Left top corner.
embedVerticalSeparationPattern(vspSize, 0, matrix);
// Right top corner.
embedVerticalSeparationPattern(matrix.getHeight() - vspSize - 1, 0, matrix);
// Left bottom corner.
embedVerticalSeparationPattern(vspSize, matrix.getHeight() - vspSize,
matrix);
}
// Embed position adjustment patterns if need be.
private static void maybeEmbedPositionAdjustmentPatterns(int version, ByteMatrix matrix)
throws WriterException {
if (version < 2) { // The patterns appear if version >= 2
return;
}
int index = version - 1;
int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index];
int numCoordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index].length;
for (int i = 0; i < numCoordinates; ++i) {
for (int j = 0; j < numCoordinates; ++j) {
int y = coordinates[i];
int x = coordinates[j];
if (x == -1 || y == -1) {
continue;
}
// If the cell is unset, we embed the position adjustment pattern here.
if (isEmpty(matrix.get(x, y))) {
// -2 is necessary since the x/y coordinates point to the center of the pattern, not the
// left top corner.
embedPositionAdjustmentPattern(x - 2, y - 2, matrix);
}
}
}
}
}

View File

@ -0,0 +1,239 @@
/*
* Copyright 2008 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.qrcode.encoder;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Mode;
/**
* @author satorux@google.com (Satoru Takabayashi) - creator
* @author dswitkin@google.com (Daniel Switkin) - ported from C++
*/
public final class QRCode {
public static final int NUM_MASK_PATTERNS = 8;
private Mode mode;
private ErrorCorrectionLevel ecLevel;
private int version;
private int matrixWidth;
private int maskPattern;
private int numTotalBytes;
private int numDataBytes;
private int numECBytes;
private int numRSBlocks;
private ByteMatrix matrix;
public QRCode() {
mode = null;
ecLevel = null;
version = -1;
matrixWidth = -1;
maskPattern = -1;
numTotalBytes = -1;
numDataBytes = -1;
numECBytes = -1;
numRSBlocks = -1;
matrix = null;
}
// Mode of the QR Code.
public Mode getMode() {
return mode;
}
// Error correction level of the QR Code.
public ErrorCorrectionLevel getECLevel() {
return ecLevel;
}
// Version of the QR Code. The bigger size, the bigger version.
public int getVersion() {
return version;
}
// ByteMatrix width of the QR Code.
public int getMatrixWidth() {
return matrixWidth;
}
// Mask pattern of the QR Code.
public int getMaskPattern() {
return maskPattern;
}
// Number of total bytes in the QR Code.
public int getNumTotalBytes() {
return numTotalBytes;
}
// Number of data bytes in the QR Code.
public int getNumDataBytes() {
return numDataBytes;
}
// Number of error correction bytes in the QR Code.
public int getNumECBytes() {
return numECBytes;
}
// Number of Reedsolomon blocks in the QR Code.
public int getNumRSBlocks() {
return numRSBlocks;
}
// ByteMatrix data of the QR Code.
public ByteMatrix getMatrix() {
return matrix;
}
// Return the value of the module (cell) pointed by "x" and "y" in the matrix of the QR Code. They
// call cells in the matrix "modules". 1 represents a black cell, and 0 represents a white cell.
public int at(int x, int y) {
// The value must be zero or one.
int value = matrix.get(x, y);
if (!(value == 0 || value == 1)) {
// this is really like an assert... not sure what better exception to use?
throw new RuntimeException("Bad value");
}
return value;
}
// Checks all the member variables are set properly. Returns true on success. Otherwise, returns
// false.
public boolean isValid() {
return
// First check if all version are not uninitialized.
mode != null &&
ecLevel != null &&
version != -1 &&
matrixWidth != -1 &&
maskPattern != -1 &&
numTotalBytes != -1 &&
numDataBytes != -1 &&
numECBytes != -1 &&
numRSBlocks != -1 &&
// Then check them in other ways..
isValidMaskPattern(maskPattern) &&
numTotalBytes == numDataBytes + numECBytes &&
// ByteMatrix stuff.
matrix != null &&
matrixWidth == matrix.getWidth() &&
// See 7.3.1 of JISX0510:2004 (p.5).
matrix.getWidth() == matrix.getHeight(); // Must be square.
}
// Return debug String.
public String toString() {
StringBuffer result = new StringBuffer(200);
result.append("<<\n");
result.append(" mode: ");
result.append(mode);
result.append("\n ecLevel: ");
result.append(ecLevel);
result.append("\n version: ");
result.append(version);
result.append("\n matrixWidth: ");
result.append(matrixWidth);
result.append("\n maskPattern: ");
result.append(maskPattern);
result.append("\n numTotalBytes: ");
result.append(numTotalBytes);
result.append("\n numDataBytes: ");
result.append(numDataBytes);
result.append("\n numECBytes: ");
result.append(numECBytes);
result.append("\n numRSBlocks: ");
result.append(numRSBlocks);
if (matrix == null) {
result.append("\n matrix: null\n");
} else {
result.append("\n matrix:\n");
result.append(matrix.toString());
}
result.append(">>\n");
return result.toString();
}
public void setMode(Mode value) {
mode = value;
}
public void setECLevel(ErrorCorrectionLevel value) {
ecLevel = value;
}
public void setVersion(int value) {
version = value;
}
public void setMatrixWidth(int value) {
matrixWidth = value;
}
public void setMaskPattern(int value) {
maskPattern = value;
}
public void setNumTotalBytes(int value) {
numTotalBytes = value;
}
public void setNumDataBytes(int value) {
numDataBytes = value;
}
public void setNumECBytes(int value) {
numECBytes = value;
}
public void setNumRSBlocks(int value) {
numRSBlocks = value;
}
// This takes ownership of the 2D array.
public void setMatrix(ByteMatrix value) {
matrix = value;
}
// Check if "mask_pattern" is valid.
public static boolean isValidMaskPattern(int maskPattern) {
return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS;
}
// Return true if the all values in the matrix are binary numbers.
//
// JAVAPORT: This is going to be super expensive and unnecessary, we should not call this in
// production. I'm leaving it because it may be useful for testing. It should be removed entirely
// if ByteMatrix is changed never to contain a -1.
/*
private static boolean EverythingIsBinary(final ByteMatrix matrix) {
for (int y = 0; y < matrix.height(); ++y) {
for (int x = 0; x < matrix.width(); ++x) {
int value = matrix.get(y, x);
if (!(value == 0 || value == 1)) {
// Found non zero/one value.
return false;
}
}
}
return true;
}
*/
}

View File

@ -28,12 +28,18 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6002 $
* Revision $Revision: 6302 $
*
*/
package net.sourceforge.plantuml;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import net.sourceforge.plantuml.version.Version;
@ -78,4 +84,22 @@ public abstract class AbstractPSystem implements PSystem {
return 1;
}
public List<File> exportDiagrams(File suggestedFile, FileFormatOption fileFormat) throws IOException, InterruptedException {
if (suggestedFile.exists() && suggestedFile.isDirectory()) {
throw new IllegalArgumentException("File is a directory " + suggestedFile);
}
OutputStream os = null;
try {
os = new FileOutputStream(suggestedFile);
this.exportDiagram(os, null, 0, fileFormat);
} finally {
if (os != null) {
os.close();
}
}
return Arrays.asList(suggestedFile);
}
}

View File

@ -45,15 +45,15 @@ public class BlockUml {
private final List<String> data;
private PSystem system;
private static final Pattern pattern1 = Pattern.compile("^@startuml\\s+\"?(.*?)\"?$");
private static final Pattern patternFilename = Pattern.compile("^@start[-\\w.]+\\s+\"?(.*?)\"?$");
BlockUml(String... strings) {
this(Arrays.asList(strings));
}
BlockUml(List<String> strings) {
public BlockUml(List<String> strings) {
final String s0 = strings.get(0).trim();
if (s0.startsWith("@startuml") == false) {
if (s0.startsWith("@start") == false) {
throw new IllegalArgumentException();
}
this.data = new ArrayList<String>(strings);
@ -63,16 +63,23 @@ public class BlockUml {
if (OptionFlags.getInstance().isWord()) {
return null;
}
final Matcher m = pattern1.matcher(data.get(0).trim());
final Matcher m = patternFilename.matcher(data.get(0).trim());
final boolean ok = m.find();
if (ok == false) {
return null;
}
return m.group(1);
final String result = m.group(1);
for (int i = 0; i < result.length(); i++) {
final char c = result.charAt(i);
if ("<>|".indexOf(c) != -1) {
return null;
}
}
return result;
}
public PSystem getSystem() throws IOException, InterruptedException {
if (system==null) {
if (system == null) {
createSystem();
}
return system;
@ -80,7 +87,7 @@ public class BlockUml {
private void createSystem() throws IOException, InterruptedException {
system = new PSystemBuilder().createPSystem(data);
}
}

View File

@ -64,27 +64,17 @@ final public class BlockUmlBuilder {
}
}
public static boolean isArobaseEnduml(String s) {
s = s.trim();
return s.equals("@enduml") || s.startsWith("@enduml ");
}
public static boolean isArobaseStartuml(String s) {
s = s.trim();
return s.equals("@startuml") || s.startsWith("@startuml ");
}
private void init(Preprocessor includer, List<String> config) throws IOException {
String s = null;
List<String> current = null;
while ((s = includer.readLine()) != null) {
if (isArobaseStartuml(s)) {
if (StartUtils.isArobaseStartDiagram(s)) {
current = new ArrayList<String>();
}
if (current != null) {
current.add(s);
}
if (isArobaseEnduml(s) && current != null) {
if (StartUtils.isArobaseEndDiagram(s) && current != null) {
current.addAll(1, config);
blocks.add(new BlockUml(current));
current = null;

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 5823 $
* Revision $Revision: 6475 $
*
*/
package net.sourceforge.plantuml;
@ -82,15 +82,17 @@ public enum ColorParam {
sequenceActorBackground(true),
sequenceActorBorder,
sequenceGroupBorder,
sequenceGroupBackground(true),
sequenceReferenceBackground(true),
sequenceDividerBackground(true),
sequenceLifeLineBackground(true),
sequenceLifeLineBorder,
sequenceParticipantBackground(true),
sequenceParticipantBorder,
sequenceArrow,
sequenceEngloberLine,
sequenceEngloberBackground(true),
sequenceBoxBorder,
sequenceBoxBackground(true),
iconPrivate,
iconPrivateBackground,

View File

@ -0,0 +1,54 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 4768 $
*
*/
package net.sourceforge.plantuml;
public enum DiagramType {
UML, DITAA, DOT, PROJECT, UNKNOWN;
static DiagramType getTypeFromArobaseStart(String s) {
if (s.startsWith("@startuml")) {
return UML;
}
if (s.startsWith("@startdot")) {
return DOT;
}
if (s.startsWith("@startditaa")) {
return DITAA;
}
if (s.startsWith("@startproject")) {
return PROJECT;
}
return UNKNOWN;
}
}

View File

@ -0,0 +1,63 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 5982 $
*
*/
package net.sourceforge.plantuml;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class EmbededDiagram implements CharSequence {
private final List<String> system;
public EmbededDiagram(List<String> system) {
this.system = new ArrayList<String>(system);
}
public int length() {
return toString().length();
}
public char charAt(int index) {
return toString().charAt(index);
}
public CharSequence subSequence(int start, int end) {
return toString().subSequence(start, end);
}
public final List<String> getLines() {
return Collections.unmodifiableList(system);
}
}

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 5942 $
* Revision $Revision: 6475 $
*
*/
package net.sourceforge.plantuml;
@ -55,11 +55,12 @@ public enum FontParam {
PACKAGE(14, Font.PLAIN, "black", null),
SEQUENCE_ACTOR(13, Font.PLAIN, "black", null),
SEQUENCE_ARROW(13, Font.PLAIN, "black", null),
SEQUENCE_ENGLOBER(13, Font.BOLD, "black", null),
SEQUENCE_BOX(13, Font.BOLD, "black", null),
SEQUENCE_DIVIDER(13, Font.BOLD, "black", null),
SEQUENCE_REFERENCE(13, Font.PLAIN, "black", null),
SEQUENCE_DELAY(11, Font.PLAIN, "black", null),
SEQUENCE_GROUPING(11, Font.BOLD, "black", null),
SEQUENCE_GROUPING_HEADER(13, Font.BOLD, "black", null),
SEQUENCE_GROUP(11, Font.BOLD, "black", null),
SEQUENCE_GROUP_HEADER(13, Font.BOLD, "black", null),
SEQUENCE_PARTICIPANT(13, Font.PLAIN, "black", null),
SEQUENCE_TITLE(13, Font.BOLD, "black", null),
STATE(14, Font.PLAIN, "black", null),

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6187 $
* Revision $Revision: 6448 $
*
*/
package net.sourceforge.plantuml;
@ -172,6 +172,8 @@ public class Option {
OptionPrint.printVersion();
} else if (s.startsWith("-D")) {
manageDefine(s.substring(2));
} else if (s.startsWith("-S")) {
manageSkinParam(s.substring(2));
} else if (s.equalsIgnoreCase("-testdot")) {
OptionPrint.printTestDot();
} else if (s.equalsIgnoreCase("-about") || s.equalsIgnoreCase("-author") || s.equalsIgnoreCase("-authors")) {
@ -211,6 +213,21 @@ public class Option {
}
}
private void manageSkinParam(String s) {
final Pattern p = Pattern.compile("^(\\w+)(?:=(.*))?$");
final Matcher m = p.matcher(s);
if (m.find()) {
skinParam(m.group(1), m.group(2));
}
}
private void skinParam(String var, String value) {
if (var != null && value != null) {
config.add("skinparamlocked " + var + " " + value);
}
}
public final File getOutputDir() {
return outputDir;
}

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6007 $
* Revision $Revision: 6448 $
*
*/
package net.sourceforge.plantuml;
@ -56,7 +56,7 @@ public class OptionPrint {
System.err.println("Usage: java -jar plantuml.jar [options] -gui");
System.err.println("\t(to execute the GUI)");
System.err.println(" or java -jar plantuml.jar [options] [files/dirs]");
System.err.println(" or java -jar plantuml.jar [options] [file/dir] [file/dir] [file/dir]");
System.err.println("\t(to process files or directories)");
System.err.println();
System.err.println("You can use the following wildcards in files/dirs:");
@ -67,7 +67,14 @@ public class OptionPrint {
System.err.println("where options include:");
System.err.println(" -gui\t\tTo run the graphical user interface");
System.err.println(" -tsvg\t\tTo generate images using SVG format");
System.err.println(" -teps\t\tTo generate images using EPS format");
System.err.println(" -txmi\t\tTo generate XMI file for classes diagrams");
System.err.println(" -tdot\t\tTo generate DOT intermediate file");
System.err.println(" -ttxt\t\tTo generate images with ASCII art");
System.err.println(" -tutxt\t\tTo generate images with ASCII art using Unicode characters");
System.err.println(" -o[utput] \"dir\"\tTo generate images in the specified directory");
System.err.println(" -DVAR1=value\tTo set a preprocessing variable as if '!define VAR1 value' were used");
System.err.println(" -Sparam1=value\tTo set a skin parameter as if 'skinparam param1 value' were used");
System.err.println(" -config \"file\"\tTo read the provided config file before each diagram");
System.err.println(" -charset xxx\tTo use a specific charset (default is " + charset + ")");
System.err.println(" -e[x]clude pattern\tTo exclude files that match the provided pattern");
@ -84,6 +91,9 @@ public class OptionPrint {
System.err.println(" -p[ipe]\t\tTo use stdin for PlantUML source and stdout for PNG/SVG generation");
System.err.println(" -computeurl\t\tTo compute the encoded URL of a PlantUML source file");
System.err.println(" -decodeurl\t\tTo retrieve the PlantUML source from an encoded URL");
System.err.println(" -syntax\t\tTo report any syntax error from standard input without generating images");
System.err.println(" -language\t\tTo print the list of PlantUML keywords");
System.err.println(" -pattern\t\tTo print the list of Regular Expression used by PlantUML");
System.err.println();
System.err.println("If needed, you can setup the environment variable GRAPHVIZ_DOT.");
exit();

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 5794 $
* Revision $Revision: 6302 $
*
*/
package net.sourceforge.plantuml;
@ -40,9 +40,9 @@ import java.util.List;
public interface PSystem {
List<File> createFiles(File suggestedFile, FileFormatOption fileFormatOption) throws IOException, InterruptedException;
List<File> exportDiagrams(File suggestedFile, FileFormatOption fileFormatOption) throws IOException, InterruptedException;
void createFile(OutputStream os, int index, FileFormatOption fileFormatOption) throws IOException;
void exportDiagram(OutputStream os, StringBuilder cmap, int index, FileFormatOption fileFormatOption) throws IOException;
int getNbImages();

View File

@ -43,6 +43,8 @@ import net.sourceforge.plantuml.activitydiagram2.ActivityDiagramFactory2;
import net.sourceforge.plantuml.classdiagram.ClassDiagramFactory;
import net.sourceforge.plantuml.componentdiagram.ComponentDiagramFactory;
import net.sourceforge.plantuml.compositediagram.CompositeDiagramFactory;
import net.sourceforge.plantuml.directdot.PSystemDotFactory;
import net.sourceforge.plantuml.ditaa.PSystemDitaaFactory;
import net.sourceforge.plantuml.eggs.PSystemEggFactory;
import net.sourceforge.plantuml.eggs.PSystemLostFactory;
import net.sourceforge.plantuml.eggs.PSystemPathFactory;
@ -51,6 +53,7 @@ import net.sourceforge.plantuml.objectdiagram.ObjectDiagramFactory;
import net.sourceforge.plantuml.oregon.PSystemOregonFactory;
import net.sourceforge.plantuml.postit.PostIdDiagramFactory;
import net.sourceforge.plantuml.printskin.PrintSkinFactory;
import net.sourceforge.plantuml.project.PSystemProjectFactory;
import net.sourceforge.plantuml.sequencediagram.SequenceDiagramFactory;
import net.sourceforge.plantuml.statediagram.StateDiagramFactory;
import net.sourceforge.plantuml.sudoku.PSystemSudokuFactory;
@ -75,16 +78,26 @@ public class PSystemBuilder {
factories.add(new PostIdDiagramFactory());
factories.add(new PrintSkinFactory());
factories.add(new PSystemVersionFactory());
factories.add(new PSystemDotFactory(DiagramType.DOT));
factories.add(new PSystemDotFactory(DiagramType.UML));
factories.add(new PSystemDitaaFactory(DiagramType.DITAA));
factories.add(new PSystemDitaaFactory(DiagramType.UML));
factories.add(new PSystemSudokuFactory());
factories.add(new PSystemEggFactory());
factories.add(new PSystemRIPFactory());
factories.add(new PSystemLostFactory());
factories.add(new PSystemPathFactory());
factories.add(new PSystemOregonFactory());
factories.add(new PSystemProjectFactory());
final UmlSource umlSource = new UmlSource(strings);
final DiagramType diagramType = umlSource.getDiagramType();
final List<PSystemError> errors = new ArrayList<PSystemError>();
for (PSystemFactory systemFactory : factories) {
final PSystem sys = new PSystemSingleBuilder(new UmlSource(strings), systemFactory).getPSystem();
if (diagramType != systemFactory.getDiagramType()) {
continue;
}
final PSystem sys = new PSystemSingleBuilder(umlSource, systemFactory).getPSystem();
if (isOk(sys)) {
return sys;
}

View File

@ -28,17 +28,14 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 5999 $
* Revision $Revision: 6453 $
*/
package net.sourceforge.plantuml;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
@ -79,24 +76,24 @@ public class PSystemError extends AbstractPSystem {
this(source, Collections.singletonList(singleError));
}
public List<File> createFiles(File suggestedFile, FileFormatOption fileFormat) throws IOException,
InterruptedException {
if (suggestedFile.exists() && suggestedFile.isDirectory()) {
throw new IllegalArgumentException("File is a directory " + suggestedFile);
}
OutputStream os = null;
try {
os = new FileOutputStream(suggestedFile);
getPngError().writeImage(os, getMetadata(), fileFormat);
} finally {
if (os != null) {
os.close();
}
}
return Arrays.asList(suggestedFile);
}
// public List<File> createFiles(File suggestedFile, FileFormatOption fileFormat) throws IOException,
// InterruptedException {
// if (suggestedFile.exists() && suggestedFile.isDirectory()) {
// throw new IllegalArgumentException("File is a directory " + suggestedFile);
// }
// OutputStream os = null;
// try {
// os = new FileOutputStream(suggestedFile);
// getPngError().writeImage(os, getMetadata(), fileFormat);
// } finally {
// if (os != null) {
// os.close();
// }
// }
// return Arrays.asList(suggestedFile);
// }
public void createFile(OutputStream os, int index, FileFormatOption fileFormat) throws IOException {
public void exportDiagram(OutputStream os, StringBuilder cmap, int index, FileFormatOption fileFormat) throws IOException {
getPngError().writeImage(os, getMetadata(), fileFormat);
}

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 3824 $
* Revision $Revision: 6341 $
*
*/
package net.sourceforge.plantuml;
@ -38,5 +38,7 @@ public interface PSystemFactory {
PSystem getSystem();
void reset();
DiagramType getDiagramType();
}

View File

@ -73,7 +73,7 @@ final public class PSystemSingleBuilder {
public PSystemSingleBuilder(UmlSource s, PSystemFactory systemFactory) throws IOException {
this.source = s;
it = s.iterator();
if (BlockUmlBuilder.isArobaseStartuml(next()) == false) {
if (StartUtils.isArobaseStartDiagram(next()) == false) {
throw new UnsupportedOperationException();
}
@ -90,7 +90,7 @@ final public class PSystemSingleBuilder {
systemFactory.reset();
while (hasNext()) {
final String s = next();
if (BlockUmlBuilder.isArobaseEnduml(s)) {
if (StartUtils.isArobaseEndDiagram(s)) {
if (source.getSize() == 2) {
assert false;
sys = buildEmptyError();
@ -130,7 +130,7 @@ final public class PSystemSingleBuilder {
systemFactory.reset();
while (hasNext()) {
final String s = next();
if (BlockUmlBuilder.isArobaseEnduml(s)) {
if (StartUtils.isArobaseEndDiagram(s)) {
final String err = ((AbstractUmlSystemCommandFactory) systemFactory).checkFinalError();
if (err != null) {
sys = buildEmptyError(err);
@ -201,7 +201,7 @@ final public class PSystemSingleBuilder {
lines.add(init);
while (hasNext()) {
final String s = next();
if (BlockUmlBuilder.isArobaseEnduml(s)) {
if (StartUtils.isArobaseEndDiagram(s)) {
return false;
}
lines.add(s);

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6228 $
* Revision $Revision: 6448 $
*
*/
package net.sourceforge.plantuml;
@ -240,7 +240,7 @@ public class SkinParam implements ISkinParam {
public static Collection<String> getPossibleValues() {
final Set<String> result = new TreeSet<String>();
result.add("Monochrome");
result.add("BackgroundColor");
// result.add("BackgroundColor");
result.add("CircledCharacterRadius");
result.add("ClassAttributeIconSize");
result.add("DefaultFontName");
@ -254,9 +254,17 @@ public class SkinParam implements ISkinParam {
result.add(h + "FontSize");
result.add(h + "FontColor");
}
for (ColorParam p : EnumSet.allOf(ColorParam.class)) {
final String h = capitalize(p.name());
result.add(h + "Color");
}
return Collections.unmodifiableSet(result);
}
private static String capitalize(String name) {
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
public int getDpi() {
final String value = getValue("dpi");
if (value != null && value.matches("\\d+")) {
@ -286,8 +294,7 @@ public class SkinParam implements ISkinParam {
}
return DotSplines.SPLINES;
}
public GraphvizLayoutStrategy getStrategy() {
final String value = getValue("layout");
if ("neato".equalsIgnoreCase(value)) {
@ -305,5 +312,4 @@ public class SkinParam implements ISkinParam {
return GraphvizLayoutStrategy.DOT;
}
}

View File

@ -106,7 +106,7 @@ public class SourceFileReader {
final File suggested = new File(outputDirectory, newName);
suggested.getParentFile().mkdirs();
for (File f : blockUml.getSystem().createFiles(suggested, fileFormatOption)) {
for (File f : blockUml.getSystem().exportDiagrams(suggested, fileFormatOption)) {
final String desc = "[" + file.getName() + "] " + blockUml.getSystem().getDescription();
final GeneratedImage generatedImage = new GeneratedImage(f, desc);
result.add(generatedImage);

View File

@ -92,7 +92,7 @@ public class SourceStringReader {
final PSystem system = b.getSystem();
final int nbInSystem = system.getNbImages();
if (numImage < nbInSystem) {
system.createFile(os, numImage, fileFormatOption);
system.exportDiagram(os, null, numImage, fileFormatOption);
return system.getDescription();
}
numImage -= nbInSystem;

View File

@ -0,0 +1,49 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6110 $
*
*/
package net.sourceforge.plantuml;
public class StartUtils {
public static boolean isArobaseStartDiagram(String s) {
s = s.trim();
return s.startsWith("@start");
}
public static boolean isArobaseEndDiagram(String s) {
s = s.trim();
return s.startsWith("@end");
}
}

View File

@ -28,15 +28,35 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6097 $
* Revision $Revision: 6382 $
*
*/
package net.sourceforge.plantuml;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import net.sourceforge.plantuml.code.Compression;
import net.sourceforge.plantuml.code.CompressionZlib;
import net.sourceforge.plantuml.graphic.HorizontalAlignement;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
public abstract class UmlDiagram extends AbstractPSystem implements PSystem {
private boolean rotation;
@ -151,4 +171,134 @@ public abstract class UmlDiagram extends AbstractPSystem implements PSystem {
this.hideUnlinkedData = hideUnlinkedData;
}
final public void exportDiagram(OutputStream os, StringBuilder cmap, int index, FileFormatOption fileFormatOption)
throws IOException {
List<BufferedImage> flashcodes = null;
try {
if ("split".equalsIgnoreCase(getSkinParam().getValue("flashcode"))
&& fileFormatOption.getFileFormat() == FileFormat.PNG) {
final String s = getSource().getPlainString();
flashcodes = exportSplitCompress(s);
} else if ("compress".equalsIgnoreCase(getSkinParam().getValue("flashcode"))
&& fileFormatOption.getFileFormat() == FileFormat.PNG) {
final String s = getSource().getPlainString();
flashcodes = exportFlashcodeCompress(s);
} else if (getSkinParam().getValue("flashcode") != null
&& fileFormatOption.getFileFormat() == FileFormat.PNG) {
final String s = getSource().getPlainString();
flashcodes = exportFlashcodeSimple(s);
}
} catch (WriterException e) {
Log.error("Cannot generate flashcode");
e.printStackTrace();
flashcodes = null;
}
exportDiagramInternal(os, cmap, index, fileFormatOption, flashcodes);
}
protected abstract void exportDiagramInternal(OutputStream os, StringBuilder cmap, int index,
FileFormatOption fileFormatOption, List<BufferedImage> flashcodes) throws IOException;
final protected void exportCmap(File suggestedFile, final StringBuilder cmap) throws FileNotFoundException {
final File cmapFile = new File(changeName(suggestedFile.getAbsolutePath()));
PrintWriter pw = null;
try {
pw = new PrintWriter(cmapFile);
pw.print(cmap.toString());
pw.close();
} finally {
if (pw != null) {
pw.close();
}
}
}
static String changeName(String name) {
return name.replaceAll("(?i)\\.\\w{3}$", ".cmapx");
}
private List<BufferedImage> exportFlashcodeSimple(String s) throws IOException, WriterException {
final QRCodeWriter writer = new QRCodeWriter();
final int multiple = 1;
final Hashtable hints = new Hashtable();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
final BitMatrix bit = writer.encode(s, BarcodeFormat.QR_CODE, multiple);
final BufferedImage im = MatrixToImageWriter.toBufferedImage(bit);
return Arrays.asList(im);
}
private List<BufferedImage> exportFlashcodeCompress(String s) throws IOException, WriterException {
final QRCodeWriter writer = new QRCodeWriter();
final int multiple = 1;
final Hashtable hints = new Hashtable();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
final Compression comp = new CompressionZlib();
final byte data[] = comp.compress(s.getBytes("UTF-8"));
// Encoder.DEFAULT_BYTE_MODE_ENCODING
final BitMatrix bit = writer.encode(new String(data, "ISO-8859-1"), BarcodeFormat.QR_CODE, multiple);
final BufferedImage im = MatrixToImageWriter.toBufferedImage(bit);
return Arrays.asList(im);
}
private List<BufferedImage> exportSplitCompress(String s) throws IOException, WriterException {
final QRCodeWriter writer = new QRCodeWriter();
final int multiple = 1;
final Hashtable hints = new Hashtable();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
final Compression comp = new CompressionZlib();
final byte data[] = comp.compress(s.getBytes("UTF-8"));
final List<BufferedImage> result = new ArrayList<BufferedImage>();
final List<byte[]> blocs = new ArrayList<byte[]>();
for (int i = 0; i < 4; i++) {
blocs.add(getSplited(data, i, 4));
}
blocs.add(xor(blocs));
for (byte d[] : blocs) {
// Encoder.DEFAULT_BYTE_MODE_ENCODING
final BitMatrix bit = writer.encode(new String(d, "ISO-8859-1"), BarcodeFormat.QR_CODE, multiple);
result.add(MatrixToImageWriter.toBufferedImage(bit));
}
return Collections.unmodifiableList(result);
}
static byte[] xor(List<byte[]> blocs) {
final byte result[] = new byte[blocs.get(0).length];
for (int i = 0; i < result.length; i++) {
result[i] = xor(blocs, i);
}
return result;
}
static byte xor(List<byte[]> blocs, int nb) {
byte result = 0;
for (byte[] bloc : blocs) {
result = (byte) (result ^ bloc[nb]);
}
return result;
}
static byte[] getSplited(byte[] data, int n, int total) {
final int size = (data.length + total - 1) / total;
assert size * total >= data.length;
final byte result[] = new byte[size + 1];
result[0] = (byte) (1 << n);
for (int i = 0; (i < size) && (n * total + i < data.length); i++) {
result[i + 1] = data[n * total + i];
}
return result;
}
}

View File

@ -33,37 +33,27 @@
*/
package net.sourceforge.plantuml;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
final public class UmlSource {
final private List<String> source = new ArrayList<String>();
@Deprecated
public UmlSource(UmlSource start) {
this.source.addAll(start.source);
}
final private List<String> source;
public UmlSource(List<String> source) {
this.source.addAll(source);
this.source = Collections.unmodifiableList(new ArrayList<String>(source));
}
@Deprecated
public UmlSource() {
public DiagramType getDiagramType() {
return DiagramType.getTypeFromArobaseStart(source.get(0));
}
public Iterator<String> iterator() {
return source.iterator();
}
@Deprecated
public void append(String s) {
source.add(s);
}
public String getPlainString() {
final StringBuilder sb = new StringBuilder();
for (String s : source) {
@ -83,10 +73,10 @@ final public class UmlSource {
public boolean isEmpty() {
for (String s : source) {
if (BlockUmlBuilder.isArobaseStartuml(s)) {
if (StartUtils.isArobaseStartDiagram(s)) {
continue;
}
if (BlockUmlBuilder.isArobaseEnduml(s)) {
if (StartUtils.isArobaseEndDiagram(s)) {
continue;
}
if (s.matches("\\s*'.*")) {

View File

@ -0,0 +1,63 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6396 $
*
*/
package net.sourceforge.plantuml;
public class Url {
private final String url;
private final String tooltip;
public Url(String url, String tooltip) {
this.url = url;
if (tooltip == null) {
this.tooltip = url;
} else {
this.tooltip = tooltip;
}
}
public final String getUrl() {
return url;
}
public final String getTooltip() {
return tooltip;
}
@Override
public String toString() {
throw new UnsupportedOperationException();
}
}

View File

@ -70,7 +70,7 @@ public class CommandLinkActivity extends SingleLineCommand2<ActivityDiagram> {
new RegexLeaf("\\s*"), //
new RegexLeaf("BACKCOLOR", "(#\\w+)?"), //
new RegexLeaf("\\s*"), //
new RegexLeaf("ARROW", "([=-]+(?:left|right|up|down|le?|ri?|up?|do?)?[=-]*\\>)"), //
new RegexLeaf("ARROW", "([=-]+(?:\\*|left|right|up|down|le?|ri?|up?|do?)?[=-]*\\>)"), //
new RegexLeaf("\\s*"), //
new RegexLeaf("BRACKET", "(?:\\[([^\\]*]+[^\\]]*)\\])?"), //
new RegexLeaf("\\s*"), //
@ -114,9 +114,15 @@ public class CommandLinkActivity extends SingleLineCommand2<ActivityDiagram> {
final String linkLabel = arg2.get("BRACKET").get(0);
final String arrow = StringUtils.manageArrowForCuca(arg2.get("ARROW").get(0));
final int lenght = arrow.length() - 1;
int lenght = arrow.length() - 1;
if (arg2.get("ARROW").get(0).contains("*")) {
lenght = 2;
}
Link link = new Link(entity1, entity2, new LinkType(LinkDecor.ARROW, LinkDecor.NONE), linkLabel, lenght);
if (arg2.get("ARROW").get(0).contains("*")) {
link.setConstraint(false);
}
final Direction direction = StringUtils.getArrowDirection(arg2.get("ARROW").get(0));
if (direction == Direction.LEFT || direction == Direction.UP) {
link = link.getInv();

View File

@ -53,12 +53,13 @@ import net.sourceforge.plantuml.cucadiagram.IEntity;
import net.sourceforge.plantuml.cucadiagram.Link;
import net.sourceforge.plantuml.cucadiagram.LinkDecor;
import net.sourceforge.plantuml.cucadiagram.LinkType;
import net.sourceforge.plantuml.cucadiagram.dot.DotMaker;
public class ActivityDiagram2 extends CucaDiagram {
private Collection<IEntity> waitings = new LinkedHashSet<IEntity>();
private ConditionalContext2 currentContext;
private int futureLength = 2;
// private int futureLength = 2;
private String futureLabel = null;
private final Collection<String> pendingLabels = new HashSet<String>();
@ -91,31 +92,46 @@ public class ActivityDiagram2 extends CucaDiagram {
}
private final Map<String, IEntity> bars = new HashMap<String, IEntity>();
public void bar(String bar) {
if (bars.containsKey(bar)) {
final IEntity existingBar = bars.get(bar);
for (Iterator<IEntity> it = waitings.iterator(); it.hasNext();) {
final IEntity w = it.next();
if (w.getType() == EntityType.SYNCHRO_BAR) {
it.remove();
}
}
afterAdd(existingBar);
return;
}
if (waitings.size() == 0) {
// throw new IllegalStateException(bar);
}
label(bar);
final Entity act = createEntity(getAutoCode(), bar, EntityType.SYNCHRO_BAR);
bars.put(bar, act);
afterAdd(act);
}
private void afterAdd(final IEntity act) {
private void afterAdd(final IEntity dest) {
for (IEntity last : this.waitings) {
// System.err.println("last=" + last);
// System.err.println("act=" + act);
this.addLink(new Link(last, act, new LinkType(LinkDecor.ARROW, LinkDecor.NONE), futureLabel, futureLength));
this.addLink(new Link(last, dest, new LinkType(LinkDecor.ARROW, LinkDecor.NONE), futureLabel, 2));
futureLabel = null;
}
for (String p : pendingLabels) {
labels.put(p, act);
labels.put(p, dest);
}
pendingLabels.clear();
this.waitings.clear();
this.waitings.add(act);
this.futureLength = 2;
this.waitings.add(dest);
// this.futureLength = 2;
}
public IEntity getLastEntityConsulted() {
@ -137,22 +153,25 @@ public class ActivityDiagram2 extends CucaDiagram {
}
public void startIf(String test, String when) {
final IEntity br = createEntity(getAutoCode(), "", EntityType.BRANCH);
final IEntity br = createEntity(getAutoCode(), test, EntityType.BRANCH);
if (DotMaker.MODE_BRANCHE_CLUSTER) {
test = null;
}
currentContext = new ConditionalContext2(currentContext, br, Direction.DOWN, when);
for (IEntity last : this.waitings) {
if (test == null) {
// this.addLink(new Link(last, br, new LinkType(LinkDecor.ARROW,
// LinkDecor.NONE), test, futureLength));
throw new IllegalArgumentException();
} else {
this.addLink(new Link(last, br, new LinkType(LinkDecor.ARROW, LinkDecor.NONE), this.futureLabel,
futureLength, null, test, getLabeldistance(), getLabelangle()));
}
// if (test == null) {
// // this.addLink(new Link(last, br, new LinkType(LinkDecor.ARROW,
// // LinkDecor.NONE), test, futureLength));
// throw new IllegalArgumentException();
// } else {
this.addLink(new Link(last, br, new LinkType(LinkDecor.ARROW, LinkDecor.NONE), this.futureLabel, 2, null,
test, getLabeldistance(), getLabelangle()));
// }
test = null;
}
this.waitings.clear();
this.waitings.add(br);
this.futureLength = 2;
// this.futureLength = 2;
this.futureLabel = when;
}
@ -161,15 +180,15 @@ public class ActivityDiagram2 extends CucaDiagram {
// }
public void endif() {
final boolean hasElse = currentContext.isHasElse();
// System.err.println("CALL endif hasElse " + hasElse);
// final boolean hasElse = currentContext.isHasElse();
// System.err.println("CALL endif hasElse " + hasElse);
this.waitings.addAll(currentContext.getPendings());
currentContext = currentContext.getParent();
// if (currentContext == null) {
// System.err.println("after endif " + currentContext);
// } else {
// System.err.println("after endif " + currentContext.getPendings());
// }
// }
}
public void else2(String when) {
@ -187,7 +206,10 @@ public class ActivityDiagram2 extends CucaDiagram {
if (pending.getLinkLabel() != null) {
this.futureLabel = pending.getLinkLabel();
}
final List<IEntity> olds = new ArrayList<IEntity>(waitings);
waitings.clear();
waitings.add(pending.getEntityFrom());
waitings.addAll(olds);
it.remove();
}
}
@ -202,10 +224,16 @@ public class ActivityDiagram2 extends CucaDiagram {
if (dest == null) {
this.pendingLinks.add(new PendingLink(last, gotoLabel, this.futureLabel));
} else {
this.addLink(new Link(last, dest, new LinkType(LinkDecor.ARROW, LinkDecor.NONE), this.futureLabel,
this.futureLength));
// final Link link = new Link(last, dest, new LinkType(LinkDecor.ARROW, LinkDecor.NONE),
// this.futureLabel,
// this.futureLength);
final Link link = new Link(last, dest, new LinkType(LinkDecor.ARROW, LinkDecor.NONE), this.futureLabel,
2);
link.setConstraint(false);
this.addLink(link);
}
}
this.futureLabel = null;
// System.err.println("Avant fin goto, waitings=" + waitings);
this.waitings.clear();
// currentContext.clearPendingsButFirst();

View File

@ -78,7 +78,7 @@ public class ConditionalContext2 {
}
public void clearPendingsButFirst() {
System.err.println("ConditionalContext2::clearPendingsButFirst");
//System.err.println("ConditionalContext2::clearPendingsButFirst");
this.pendings.clear();
pendings.add(branch);
}
@ -90,7 +90,7 @@ public class ConditionalContext2 {
throw new IllegalStateException();
}
this.hasElse = true;
System.err.println("pend=" + pendings);
//System.err.println("pend=" + pendings);
if (pendings.size() == 0) {
throw new IllegalStateException();
}

View File

@ -59,4 +59,9 @@ public class PendingLink {
return linkLabel;
}
@Override
public String toString() {
return entityFrom + " -> " + gotoLabel + " " + linkLabel;
}
}

View File

@ -42,7 +42,7 @@ import net.sourceforge.plantuml.command.SingleLineCommand;
public class CommandNewActivity2 extends SingleLineCommand<ActivityDiagram2> {
public CommandNewActivity2(ActivityDiagram2 diagram) {
super(diagram, "(?i)^[\"<](.+)[\">]$");
super(diagram, "(?i)^\\s*[-*]\\s*([^\"\\s].*)$");
}
@Override

View File

@ -43,7 +43,7 @@ import net.sourceforge.plantuml.command.CommandMultilines;
public class CommandNewMultilinesActivity2 extends CommandMultilines<ActivityDiagram2> {
public CommandNewMultilinesActivity2(final ActivityDiagram2 system) {
super(system, "(?i)^[\"<].*$", "(?i)^.*[\">]$");
super(system, "(?i)^\\s*[-*]\\s*\"\\s*.*$", "(?i)^.*\\s*\"\\s*$");
}
public final CommandExecutionResult execute(List<String> lines) {
@ -55,6 +55,12 @@ public class CommandNewMultilinesActivity2 extends CommandMultilines<ActivityDia
return CommandExecutionResult.error("Unreachable statement");
}
String s = StringUtils.getMergedLines(lines);
s = s.trim();
assert s.startsWith("-") || s.startsWith("*") ;
s = s.substring(1);
s = s.trim();
assert s.startsWith("\"");
assert s.endsWith("\"");
s = s.substring(1, s.length() - 2);
getSystem().newActivity(s);

View File

@ -0,0 +1,42 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6170 $
*
*/
package net.sourceforge.plantuml.braille;
public class BrailleUtils {
public static boolean isBraille(char c) {
return Character.UnicodeBlock.of(c) == Character.UnicodeBlock.BRAILLE_PATTERNS;
}
}

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6169 $
* Revision $Revision: 6451 $
*
*/
package net.sourceforge.plantuml.classdiagram;
@ -41,7 +41,7 @@ import net.sourceforge.plantuml.classdiagram.command.CommandHideShow;
import net.sourceforge.plantuml.classdiagram.command.CommandHideShow3;
import net.sourceforge.plantuml.classdiagram.command.CommandImport;
import net.sourceforge.plantuml.classdiagram.command.CommandLinkClass2;
import net.sourceforge.plantuml.classdiagram.command.CommandLinkLollipop;
import net.sourceforge.plantuml.classdiagram.command.CommandLinkLollipop2;
import net.sourceforge.plantuml.classdiagram.command.CommandMultilinesClassNote;
import net.sourceforge.plantuml.classdiagram.command.CommandNamespace;
import net.sourceforge.plantuml.classdiagram.command.CommandStereotype;
@ -83,7 +83,7 @@ public class ClassDiagramFactory extends AbstractUmlSystemCommandFactory {
//addCommand(new CommandLinkClass(system));
addCommand(new CommandLinkClass2(system));
addCommand(new CommandLinkLollipop(system));
addCommand(new CommandLinkLollipop2(system));
addCommand(new CommandImport(system));
addCommand(new CommandNoteEntity(system));

View File

@ -45,22 +45,32 @@ import net.sourceforge.plantuml.command.regex.RegexOr;
import net.sourceforge.plantuml.command.regex.RegexPartialMatch;
import net.sourceforge.plantuml.cucadiagram.Entity;
import net.sourceforge.plantuml.cucadiagram.EntityType;
import net.sourceforge.plantuml.cucadiagram.IEntity;
import net.sourceforge.plantuml.cucadiagram.Link;
import net.sourceforge.plantuml.cucadiagram.LinkDecor;
import net.sourceforge.plantuml.cucadiagram.LinkType;
import net.sourceforge.plantuml.cucadiagram.Stereotype;
public class CommandCreateEntityClass2 extends SingleLineCommand2<ClassDiagram> {
enum Mode {
EXTENDS, IMPLEMENTS
};
public CommandCreateEntityClass2(ClassDiagram diagram) {
super(diagram, getRegexConcat());
}
private static RegexConcat getRegexConcat() {
return new RegexConcat(new RegexLeaf("^"),
new RegexLeaf("TYPE", "(interface|enum|abstract\\s+class|abstract|class)\\s+"),
new RegexOr(
new RegexLeaf("NAME1", "(?:\"([^\"]+)\"\\s+as\\s+)?(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)"),
new RegexLeaf("NAME2", "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s+as\\s+\"([^\"]+)\""),
new RegexLeaf("NAME3", "\"([^\"]+)\"")),
new RegexLeaf("STEREO", "(?:\\s*([\\<\\[]{2}.*[\\>\\]]{2}))?"),
return new RegexConcat(new RegexLeaf("^"), //
new RegexLeaf("TYPE",//
"(interface|enum|abstract\\s+class|abstract|class)\\s+"), //
new RegexOr(new RegexLeaf("NAME1",
"(?:\"([^\"]+)\"\\s+as\\s+)?(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)"), //
new RegexLeaf("NAME2", "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s+as\\s+\"([^\"]+)\""), //
new RegexLeaf("NAME3", "\"([^\"]+)\"")), //
new RegexLeaf("STEREO", "(?:\\s*([\\<\\[]{2}.*[\\>\\]]{2}))?"), //
new RegexLeaf("EXTENDS", "(\\s+(extends|implements)\\s+(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*))?"), //
new RegexLeaf("$"));
}
@ -91,9 +101,34 @@ public class CommandCreateEntityClass2 extends SingleLineCommand2<ClassDiagram>
entity.setStereotype(new Stereotype(stereotype, getSystem().getSkinParam().getCircledCharacterRadius(),
getSystem().getSkinParam().getFont(FontParam.CIRCLED_CHARACTER, null)));
}
manageExtends(getSystem(), arg, entity);
return CommandExecutionResult.ok();
}
public static void manageExtends(ClassDiagram system, Map<String, RegexPartialMatch> arg, final Entity entity) {
if (arg.get("EXTENDS").get(1) != null) {
final Mode mode = arg.get("EXTENDS").get(1).equalsIgnoreCase("extends") ? Mode.EXTENDS : Mode.IMPLEMENTS;
final String other = arg.get("EXTENDS").get(2);
EntityType type2 = EntityType.CLASS;
if (mode == Mode.IMPLEMENTS) {
type2 = EntityType.INTERFACE;
}
if (mode == Mode.EXTENDS && entity.getType() == EntityType.INTERFACE) {
type2 = EntityType.INTERFACE;
}
final IEntity cl2 = system.getOrCreateClass(other, type2);
LinkType typeLink = new LinkType(LinkDecor.NONE, LinkDecor.EXTENDS);
if (type2 == EntityType.INTERFACE && entity.getType() != EntityType.INTERFACE) {
typeLink = typeLink.getDashed();
}
final Link link = new Link(cl2, entity, typeLink, null, 2, null, null, system.getLabeldistance(),
system.getLabelangle());
system.addLink(link);
}
}
// @Override
// protected CommandExecutionResult executeArg(List<String> arg) {
// final String arg0 = arg.get(0).toUpperCase();

View File

@ -55,19 +55,19 @@ public class CommandCreateEntityClassMultilines2 extends CommandMultilines2<Clas
public CommandCreateEntityClassMultilines2(ClassDiagram diagram) {
super(diagram, getRegexConcat(), "(?i)^\\s*\\}\\s*$");
}
private static RegexConcat getRegexConcat() {
return new RegexConcat(new RegexLeaf("^"),
new RegexLeaf("TYPE", "(interface|enum|abstract\\s+class|abstract|class)\\s+"),
new RegexOr(
new RegexLeaf("NAME1", "(?:\"([^\"]+)\"\\s+as\\s+)?(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)"),
new RegexLeaf("NAME2", "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s+as\\s+\"([^\"]+)\""),
new RegexLeaf("NAME3", "\"([^\"]+)\"")),
new RegexLeaf("STEREO", "(?:\\s*([\\<\\[]{2}.*[\\>\\]]{2}))?"),
return new RegexConcat(new RegexLeaf("^"), //
new RegexLeaf("TYPE", "(interface|enum|abstract\\s+class|abstract|class)\\s+"), //
new RegexOr( //
new RegexLeaf("NAME1", "(?:\"([^\"]+)\"\\s+as\\s+)?(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)"), //
new RegexLeaf("NAME2", "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s+as\\s+\"([^\"]+)\""), //
new RegexLeaf("NAME3", "\"([^\"]+)\"")), //
new RegexLeaf("STEREO", "(?:\\s*([\\<\\[]{2}.*[\\>\\]]{2}))?"), //
new RegexLeaf("EXTENDS", "(\\s+(extends|implements)\\s+(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*))?"), //
new RegexLeaf("\\s*\\{\\s*$"));
}
public CommandExecutionResult execute(List<String> lines) {
StringUtils.trim(lines, true);
final Map<String, RegexPartialMatch> line0 = getStartingPattern().matcher(lines.get(0).trim());
@ -82,11 +82,14 @@ public class CommandCreateEntityClassMultilines2 extends CommandMultilines2<Clas
}
entity.addFieldOrMethod(s);
}
CommandCreateEntityClass2.manageExtends(getSystem(), line0, entity);
return CommandExecutionResult.ok();
}
private Entity executeArg0(Map<String, RegexPartialMatch> arg) {
final EntityType type = EntityType.getEntityType(arg.get("TYPE").get(0).toUpperCase());
final String code;
final String display;

View File

@ -72,8 +72,9 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
new RegexLeaf("\\s*"),
new RegexLeaf("FIRST_LABEL", "(?:\"([^\"]+)\")?"),
new RegexLeaf("\\s*"),
new RegexOr(new RegexLeaf("LEFT_TO_RIGHT",
"(([-=.]+)(?:(left|right|up|down|le?|ri?|up?|do?)(?=[-=.]))?([-=.]*)(o +|[\\]>*+]|\\|[>\\]])?)"),
new RegexOr(
new RegexLeaf("LEFT_TO_RIGHT",
"(([-=.]+)(?:(left|right|up|down|le?|ri?|up?|do?)(?=[-=.]))?([-=.]*)(o +|[\\]>*+]|\\|[>\\]])?)"),
new RegexLeaf("RIGHT_TO_LEFT",
"(( +o|[\\[<*+]|[<\\[]\\|)?([-=.]*)(left|right|up|down|le?|ri?|up?|do?)?([-=.]+))"),
new RegexLeaf("NAV_AGREG_OR_COMPO_INV",
@ -88,7 +89,11 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
+ "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*|\"[^\"]+\")\\s*(\\<\\<.*\\>\\>)?"),
new RegexLeaf("COUPLE2",
"\\(\\s*(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s*,\\s*(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s*\\)")),
new RegexLeaf("\\s*"), new RegexLeaf("LABEL_LINK", "(?::\\s*([^\"]+))?$"));
// new RegexLeaf("\\s*"), new RegexLeaf("LABEL_LINK",
// "(?::\\s*([^\"]+))?$"));
new RegexLeaf("\\s*"), new RegexOr(null, true, new RegexLeaf("LABEL_LINK", ":\\s*([^\"]+)"),
new RegexLeaf("LABEL_LINK_XT", ":\\s*(\"[^\"]*\")?\\s*([^\"]*)\\s*(\"[^\"]*\")?")),
new RegexLeaf("$"));
}
private static String optionalKeywords(UmlDiagramType type) {
@ -160,8 +165,22 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
dir = dir.getInv();
}
Link link = new Link(cl1, cl2, linkType, arg.get("LABEL_LINK").get(0), queue.length(), arg.get("FIRST_LABEL")
.get(0), arg.get("SECOND_LABEL").get(0), getSystem().getLabeldistance(), getSystem().getLabelangle());
String firstLabel = arg.get("FIRST_LABEL").get(0);
String secondLabel = arg.get("SECOND_LABEL").get(0);
String labelLink = null;
if (arg.get("LABEL_LINK").get(0) != null) {
labelLink = arg.get("LABEL_LINK").get(0);
} else if (arg.get("LABEL_LINK_XT").get(0) != null || arg.get("LABEL_LINK_XT").get(1) != null
|| arg.get("LABEL_LINK_XT").get(2) != null) {
labelLink = arg.get("LABEL_LINK_XT").get(1);
firstLabel = merge(firstLabel, arg.get("LABEL_LINK_XT").get(0));
secondLabel = merge(arg.get("LABEL_LINK_XT").get(2), secondLabel);
}
Link link = new Link(cl1, cl2, linkType, labelLink, queue.length(), firstLabel, secondLabel, getSystem()
.getLabeldistance(), getSystem().getLabelangle());
if (dir == Direction.LEFT || dir == Direction.UP) {
link = link.getInv();
@ -173,6 +192,20 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
return CommandExecutionResult.ok();
}
private String merge(String a, String b) {
if (a == null && b == null) {
return null;
}
if (a == null && b != null) {
return StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(b);
}
if (b == null && a != null) {
return StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(a);
}
return StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(a) + "\\n"
+ StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(b);
}
private void addLink(Link link, String weight) {
getSystem().addLink(link);
if (weight == null) {
@ -196,8 +229,8 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
}
private CommandExecutionResult executePackageLink(Map<String, RegexPartialMatch> arg) {
final String ent1 = arg.get("ENT1").get(1);
final String ent2 = arg.get("ENT2").get(1);
final String ent1 = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.get("ENT1").get(1));
final String ent2 = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.get("ENT2").get(1));
final Group cl1 = getSystem().getGroup(ent1);
final Group cl2 = getSystem().getGroup(ent2);
@ -210,9 +243,11 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
queue = getQueue(arg);
}
Link link = new Link(cl1.getEntityCluster(), cl2.getEntityCluster(), linkType, arg.get("LABEL_LINK").get(0),
queue.length(), arg.get("FIRST_LABEL").get(0), arg.get("SECOND_LABEL").get(0), getSystem()
.getLabeldistance(), getSystem().getLabelangle());
final String labelLink = arg.get("LABEL_LINK").get(0);
final String firstLabel = arg.get("FIRST_LABEL").get(0);
final String secondLabel = arg.get("SECOND_LABEL").get(0);
Link link = new Link(cl1.getEntityCluster(), cl2.getEntityCluster(), linkType, labelLink, queue.length(),
firstLabel, secondLabel, getSystem().getLabeldistance(), getSystem().getLabelangle());
if (dir == Direction.LEFT || dir == Direction.UP) {
link = link.getInv();
}
@ -232,7 +267,7 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
return CommandExecutionResult.error("No class " + clName2);
}
final String ent2 = arg.get("ENT2").get(1);
final String ent2 = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.get("ENT2").get(1));
final IEntity cl2 = getSystem().getOrCreateClass(ent2);
final LinkType linkType = getLinkType(arg);
@ -258,7 +293,7 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
return CommandExecutionResult.error("No class " + clName2);
}
final String ent1 = arg.get("ENT1").get(1);
final String ent1 = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.get("ENT1").get(1));
final IEntity cl1 = getSystem().getOrCreateClass(ent1);
final LinkType linkType = getLinkType(arg);

View File

@ -70,7 +70,7 @@ final public class CommandLinkLollipop extends SingleLineCommand<AbstractClassOr
private final Pattern patternAssociationPoint = Pattern
.compile("\\(\\s*(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s*,\\s*(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s*\\)");
public CommandLinkLollipop(AbstractClassOrObjectDiagram diagram) {
private CommandLinkLollipop(AbstractClassOrObjectDiagram diagram) {
super(
diagram,
"(?i)^(?:@([\\d.])\\s+)?((?:(interface|enum|abstract\\s+class|abstract|class)\\s+)?(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)|\\(\\s*\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*\\s*,\\s*\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*\\s*\\))\\s*(?:\"([^\"]+)\")?\\s*"

View File

@ -0,0 +1,193 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 5436 $
*
*/
package net.sourceforge.plantuml.classdiagram.command;
import java.util.Map;
import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.UmlDiagramType;
import net.sourceforge.plantuml.UniqueSequence;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.command.SingleLineCommand2;
import net.sourceforge.plantuml.command.regex.RegexConcat;
import net.sourceforge.plantuml.command.regex.RegexLeaf;
import net.sourceforge.plantuml.command.regex.RegexOr;
import net.sourceforge.plantuml.command.regex.RegexPartialMatch;
import net.sourceforge.plantuml.cucadiagram.Entity;
import net.sourceforge.plantuml.cucadiagram.EntityType;
import net.sourceforge.plantuml.cucadiagram.Link;
import net.sourceforge.plantuml.cucadiagram.LinkDecor;
import net.sourceforge.plantuml.cucadiagram.LinkType;
import net.sourceforge.plantuml.objectdiagram.AbstractClassOrObjectDiagram;
final public class CommandLinkLollipop2 extends SingleLineCommand2<AbstractClassOrObjectDiagram> {
public CommandLinkLollipop2(AbstractClassOrObjectDiagram diagram) {
super(diagram, getRegexConcat(diagram.getUmlDiagramType()));
}
static RegexConcat getRegexConcat(UmlDiagramType umlDiagramType) {
return new RegexConcat(new RegexLeaf("HEADER", "^(?:@([\\d.]+)\\s+)?"), //
new RegexLeaf("ENT1", "(?:" + optionalKeywords(umlDiagramType) + "\\s+)?"
+ "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*|\"[^\"]+\")\\s*(\\<\\<.*\\>\\>)?"), //
new RegexLeaf("\\s*"), //
new RegexLeaf("FIRST_LABEL", "(?:\"([^\"]+)\")?"), //
new RegexLeaf("\\s*"), //
new RegexOr(new RegexLeaf("LOL_THEN_ENT", "\\(\\)([-=.]+)"), //
new RegexLeaf("ENT_THEN_LOL", "([-=.]+)\\(\\)")), //
new RegexLeaf("\\s*"), //
new RegexLeaf("SECOND_LABEL", "(?:\"([^\"]+)\")?"), //
new RegexLeaf("\\s*"), //
new RegexLeaf("ENT2", "(?:" + optionalKeywords(umlDiagramType) + "\\s+)?"
+ "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*|\"[^\"]+\")\\s*(\\<\\<.*\\>\\>)?"), //
new RegexLeaf("\\s*"), //
new RegexOr(null, true, //
new RegexLeaf("LABEL_LINK", ":\\s*([^\"]+)"), //
new RegexLeaf("LABEL_LINK_XT", ":\\s*(\"[^\"]*\")?\\s*([^\"]*)\\s*(\"[^\"]*\")?")), //
new RegexLeaf("$"));
}
private static String optionalKeywords(UmlDiagramType type) {
if (type == UmlDiagramType.CLASS) {
return "(interface|enum|abstract\\s+class|abstract|class)";
}
if (type == UmlDiagramType.OBJECT) {
return "(object)";
}
throw new IllegalArgumentException();
}
@Override
protected CommandExecutionResult executeArg(Map<String, RegexPartialMatch> arg) {
final String ent1 = arg.get("ENT1").get(1);
final String ent2 = arg.get("ENT2").get(1);
final Entity cl1;
final Entity cl2;
final Entity normalEntity;
final String suffix = "lol" + UniqueSequence.getValue();
if (arg.get("LOL_THEN_ENT").get(0) != null) {
cl2 = (Entity) getSystem().getOrCreateClass(ent2);
cl1 = getSystem().createEntity(cl2.getCode() + suffix, ent1, EntityType.LOLLIPOP);
normalEntity = cl2;
} else {
assert arg.get("ENT_THEN_LOL").get(0) != null;
cl1 = (Entity) getSystem().getOrCreateClass(ent1);
cl2 = getSystem().createEntity(cl1.getCode() + suffix, ent2, EntityType.LOLLIPOP);
normalEntity = cl1;
}
final LinkType linkType = getLinkType(arg);
final String queue = getQueue(arg);
int length = queue.length();
if (length == 1 && getSystem().getNbOfHozizontalLollipop(normalEntity) > 1) {
length++;
}
String firstLabel = arg.get("FIRST_LABEL").get(0);
String secondLabel = arg.get("SECOND_LABEL").get(0);
String labelLink = null;
if (arg.get("LABEL_LINK").get(0) != null) {
labelLink = arg.get("LABEL_LINK").get(0);
} else if (arg.get("LABEL_LINK_XT").get(0) != null || arg.get("LABEL_LINK_XT").get(1) != null
|| arg.get("LABEL_LINK_XT").get(2) != null) {
labelLink = arg.get("LABEL_LINK_XT").get(1);
firstLabel = merge(firstLabel, arg.get("LABEL_LINK_XT").get(0));
secondLabel = merge(arg.get("LABEL_LINK_XT").get(2), secondLabel);
}
final Link link = new Link(cl1, cl2, linkType, labelLink, length, firstLabel, secondLabel, getSystem()
.getLabeldistance(), getSystem().getLabelangle());
getSystem().resetPragmaLabel();
addLink(link, arg.get("HEADER").get(0));
return CommandExecutionResult.ok();
}
private String merge(String a, String b) {
if (a == null && b == null) {
return null;
}
if (a == null && b != null) {
return StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(b);
}
if (b == null && a != null) {
return StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(a);
}
return StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(a) + "\\n"
+ StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(b);
}
private void addLink(Link link, String weight) {
getSystem().addLink(link);
if (weight == null) {
final LinkType type = link.getType();
// --|> highest
// --*, -->, --o normal
// ..*, ..>, ..o lowest
// if (type.isDashed() == false) {
// if (type.contains(LinkDecor.EXTENDS)) {
// link.setWeight(3);
// }
// if (type.contains(LinkDecor.ARROW) ||
// type.contains(LinkDecor.COMPOSITION)
// || type.contains(LinkDecor.AGREGATION)) {
// link.setWeight(2);
// }
// }
} else {
link.setWeight(Double.parseDouble(weight));
}
}
private LinkType getLinkType(Map<String, RegexPartialMatch> arg) {
return new LinkType(LinkDecor.NONE, LinkDecor.NONE);
}
private String getQueue(Map<String, RegexPartialMatch> arg) {
if (arg.get("LOL_THEN_ENT").get(0) != null) {
return arg.get("LOL_THEN_ENT").get(0).trim();
}
if (arg.get("ENT_THEN_LOL").get(0) != null) {
return arg.get("ENT_THEN_LOL").get(0).trim();
}
throw new IllegalArgumentException();
}
}

View File

@ -35,23 +35,32 @@ package net.sourceforge.plantuml.classdiagram.command;
import java.util.List;
import net.sourceforge.plantuml.classdiagram.ClassDiagram;
import net.sourceforge.plantuml.Url;
import net.sourceforge.plantuml.classdiagram.AbstractEntityDiagram;
import net.sourceforge.plantuml.command.CommandExecutionResult;
import net.sourceforge.plantuml.command.SingleLineCommand;
import net.sourceforge.plantuml.cucadiagram.Entity;
public class CommandUrl extends SingleLineCommand<ClassDiagram> {
public class CommandUrl extends SingleLineCommand<AbstractEntityDiagram> {
public CommandUrl(ClassDiagram classDiagram) {
super(classDiagram, "(?i)^url\\s*(?:of|for)?\\s+([\\p{L}0-9_.]+|\"[^\"]+\")\\s+(?:is)?\\s*\\[\\[(.*)\\]\\]$");
public CommandUrl(AbstractEntityDiagram diagram) {
super(diagram,
"(?i)^url\\s*(?:of|for)?\\s+([\\p{L}0-9_.]+|\"[^\"]+\")\\s+(?:is)?\\s*\\[\\[([^|]*)(?:\\|([^|]*))?\\]\\]$");
}
@Override
protected CommandExecutionResult executeArg(List<String> arg) {
final String code = arg.get(0);
final String url = arg.get(1);
String url = arg.get(1);
final String title = arg.get(2);
final Entity entity = (Entity) getSystem().getOrCreateClass(code);
entity.setUrl(url);
if (url.startsWith("http:") == false && url.startsWith("https:") == false) {
final String top = getSystem().getSkinParam().getValue("topurl");
if (top != null) {
url = top + url;
}
}
entity.setUrl(new Url(url, title));
return CommandExecutionResult.ok();
}

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 5980 $
* Revision $Revision: 6362 $
*
*/
package net.sourceforge.plantuml.code;
@ -50,7 +50,16 @@ public class ArobaseStringCompressor implements StringCompressor {
}
public String decompress(String s) throws IOException {
return clean(s);
String result = clean(s);
if (result.startsWith("@start")) {
return result;
}
result = "@startuml\n" + result;
if (result.endsWith("\n") == false) {
result += "\n";
}
result += "@enduml";
return result;
}
private String clean(String s) {
@ -70,5 +79,4 @@ public class ArobaseStringCompressor implements StringCompressor {
return s;
}
}

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 5751 $
* Revision $Revision: 6486 $
*
*/
package net.sourceforge.plantuml.command;
@ -37,6 +37,7 @@ import java.util.List;
import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.UniqueSequence;
import net.sourceforge.plantuml.Url;
import net.sourceforge.plantuml.classdiagram.AbstractEntityDiagram;
import net.sourceforge.plantuml.cucadiagram.Entity;
import net.sourceforge.plantuml.cucadiagram.EntityType;
@ -44,6 +45,7 @@ import net.sourceforge.plantuml.cucadiagram.IEntity;
import net.sourceforge.plantuml.cucadiagram.Link;
import net.sourceforge.plantuml.cucadiagram.LinkDecor;
import net.sourceforge.plantuml.cucadiagram.LinkType;
import net.sourceforge.plantuml.sequencediagram.Note;
public abstract class AbstractCommandMultilinesNoteEntity extends CommandMultilines<AbstractEntityDiagram> {
@ -58,11 +60,20 @@ public abstract class AbstractCommandMultilinesNoteEntity extends CommandMultili
final IEntity cl1 = getSystem().getOrCreateClass(line0.get(1));
final List<String> strings = StringUtils.removeEmptyColumns(lines.subList(1, lines.size() - 1));
List<String> strings = StringUtils.removeEmptyColumns(lines.subList(1, lines.size() - 1));
Url url = null;
if (strings.size() > 0) {
url = Note.extractUrl(strings.get(0));
}
if (url != null) {
strings = strings.subList(1, strings.size());
}
final String s = StringUtils.getMergedLines(strings);
final Entity note = getSystem().createEntity("GMN" + UniqueSequence.getValue(), s, EntityType.NOTE);
note.setSpecificBackcolor(line0.get(2));
note.setUrl(url);
final Position position = Position.valueOf(pos.toUpperCase()).withRankdir(getSystem().getRankdir());
final Link link;

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6214 $
* Revision $Revision: 6423 $
*
*/
package net.sourceforge.plantuml.command;
@ -38,11 +38,19 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import net.sourceforge.plantuml.DiagramType;
import net.sourceforge.plantuml.UmlDiagram;
public abstract class AbstractUmlSystemCommandFactory implements PSystemCommandFactory {
private final DiagramType type;
protected AbstractUmlSystemCommandFactory() {
this(DiagramType.UML);
}
protected AbstractUmlSystemCommandFactory(DiagramType type) {
this.type = type;
reset();
}
@ -120,4 +128,8 @@ public abstract class AbstractUmlSystemCommandFactory implements PSystemCommandF
}
final public DiagramType getDiagramType() {
return type;
}
}

View File

@ -35,11 +35,11 @@ package net.sourceforge.plantuml.command;
import java.util.List;
import net.sourceforge.plantuml.UmlDiagram;
import net.sourceforge.plantuml.PSystem;
public class CommandComment extends SingleLineCommand<UmlDiagram> {
public class CommandComment extends SingleLineCommand<PSystem> {
public CommandComment(UmlDiagram diagram) {
public CommandComment(PSystem diagram) {
super(diagram, "(?i)^\\s*('.*||/'.*'/\\s*)$");
}

View File

@ -35,11 +35,11 @@ package net.sourceforge.plantuml.command;
import java.util.List;
import net.sourceforge.plantuml.UmlDiagram;
import net.sourceforge.plantuml.PSystem;
public class CommandNope extends SingleLineCommand<UmlDiagram> {
public class CommandNope extends SingleLineCommand<PSystem> {
public CommandNope(UmlDiagram diagram) {
public CommandNope(PSystem diagram) {
super(diagram, "(?i)^\\s*$");
}

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 5769 $
* Revision $Revision: 6448 $
*
*/
package net.sourceforge.plantuml.command;
@ -40,12 +40,13 @@ import net.sourceforge.plantuml.UmlDiagram;
public class CommandSkinParam extends SingleLineCommand<UmlDiagram> {
public CommandSkinParam(UmlDiagram diagram) {
super(diagram, "(?i)^skinparam\\s+([\\w.]*(?:\\<\\<.*\\>\\>)?[\\w.]*)\\s+([^{}]*)$");
super(diagram, "(?i)^(skinparam|skinparamlocked)\\s+([\\w.]*(?:\\<\\<.*\\>\\>)?[\\w.]*)\\s+([^{}]*)$");
}
@Override
protected CommandExecutionResult executeArg(List<String> arg) {
getSystem().setParam(arg.get(0), arg.get(1));
boolean locked = arg.get(0).endsWith("locked");
getSystem().setParam(arg.get(1), arg.get(2));
return CommandExecutionResult.ok();
}

View File

@ -28,14 +28,12 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6186 $
* Revision $Revision: 6280 $
*
*/
package net.sourceforge.plantuml.command;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -44,8 +42,6 @@ import net.sourceforge.plantuml.StringUtils;
public abstract class SingleLineCommand<S extends PSystem> implements Command {
private static final Set<String> printed = new HashSet<String>();
private final S system;
private final Pattern pattern;
@ -60,10 +56,6 @@ public abstract class SingleLineCommand<S extends PSystem> implements Command {
throw new IllegalArgumentException("Bad pattern " + pattern);
}
if (printed.add(pattern) == true) {
// System.out.println(pattern);
}
this.system = system;
this.pattern = Pattern.compile(pattern);
}

View File

@ -28,11 +28,12 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 5463 $
* Revision $Revision: 6396 $
*
*/
package net.sourceforge.plantuml.componentdiagram;
import net.sourceforge.plantuml.classdiagram.command.CommandUrl;
import net.sourceforge.plantuml.command.AbstractUmlSystemCommandFactory;
import net.sourceforge.plantuml.command.CommandCreateNote;
import net.sourceforge.plantuml.command.CommandEndPackage;
@ -71,6 +72,7 @@ public class ComponentDiagramFactory extends AbstractUmlSystemCommandFactory {
addCommand(new CommandNoteEntity(system));
addCommand(new CommandCreateNote(system));
addCommand(new CommandUrl(system));
addCommand(new CommandCreateComponent(system));
addCommand(new CommandCreateCircleInterface(system));
addCommand(new CommandCreateActorInComponent(system));

View File

@ -28,15 +28,18 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6229 $
* Revision $Revision: 6453 $
*
*/
package net.sourceforge.plantuml.cucadiagram;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@ -52,13 +55,13 @@ import java.util.TreeMap;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.Log;
import net.sourceforge.plantuml.OptionFlags;
import net.sourceforge.plantuml.UmlDiagram;
import net.sourceforge.plantuml.UmlDiagramType;
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramFileMaker;
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramFileMakerBeta;
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramPngMaker3;
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramTxtMaker;
import net.sourceforge.plantuml.png.PngSplitter;
import net.sourceforge.plantuml.skin.VisibilityModifier;
import net.sourceforge.plantuml.xmi.CucaDiagramXmiMaker;
@ -272,33 +275,37 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
abstract protected List<String> getDotStrings();
final public List<File> createFiles(File suggestedFile, FileFormatOption fileFormatOption) throws IOException,
InterruptedException {
// final public List<File> createFiles(File suggestedFile, FileFormatOption
// fileFormatOption) throws IOException,
// InterruptedException {
//
// final FileFormat fileFormat = fileFormatOption.getFileFormat();
//
// if (fileFormat == FileFormat.ATXT || fileFormat == FileFormat.UTXT) {
// return createFilesTxt(suggestedFile, fileFormat);
// }
//
// if (fileFormat.name().startsWith("XMI")) {
// return createFilesXmi(suggestedFile, fileFormat);
// }
//
// if (OptionFlags.getInstance().useJavaInsteadOfDot()) {
// return createPng2(suggestedFile);
// }
// if (getUmlDiagramType() == UmlDiagramType.COMPOSITE || (BETA &&
// getUmlDiagramType() == UmlDiagramType.CLASS)) {
// final CucaDiagramFileMakerBeta maker = new
// CucaDiagramFileMakerBeta(this);
// return maker.createFile(suggestedFile, getDotStrings(), fileFormat);
// }
// final CucaDiagramFileMaker maker = new CucaDiagramFileMaker(this);
// return maker.createFile(suggestedFile, getDotStrings(),
// fileFormatOption);
// }
final FileFormat fileFormat = fileFormatOption.getFileFormat();
if (fileFormat == FileFormat.ATXT || fileFormat == FileFormat.UTXT) {
return createFilesTxt(suggestedFile, fileFormat);
}
if (fileFormat.name().startsWith("XMI")) {
return createFilesXmi(suggestedFile, fileFormat);
}
if (OptionFlags.getInstance().useJavaInsteadOfDot()) {
return createPng2(suggestedFile);
}
if (getUmlDiagramType() == UmlDiagramType.COMPOSITE || (BETA && getUmlDiagramType() == UmlDiagramType.CLASS)) {
final CucaDiagramFileMakerBeta maker = new CucaDiagramFileMakerBeta(this);
return maker.createFile(suggestedFile, getDotStrings(), fileFormat);
}
final CucaDiagramFileMaker maker = new CucaDiagramFileMaker(this);
return maker.createFile(suggestedFile, getDotStrings(), fileFormatOption);
}
private List<File> createFilesXmi(File suggestedFile, FileFormat fileFormat) throws IOException {
private void createFilesXmi(OutputStream suggestedFile, FileFormat fileFormat) throws IOException {
final CucaDiagramXmiMaker maker = new CucaDiagramXmiMaker(this, fileFormat);
return maker.createFiles(suggestedFile);
maker.createFiles(suggestedFile);
}
private List<File> createFilesTxt(File suggestedFile, FileFormat fileFormat) throws IOException {
@ -308,13 +315,64 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
public static boolean BETA;
final public void createFile(OutputStream os, int index, FileFormatOption fileFormatOption) throws IOException {
@Override
public List<File> exportDiagrams(File suggestedFile, FileFormatOption fileFormat) throws IOException,
InterruptedException {
if (suggestedFile.exists() && suggestedFile.isDirectory()) {
throw new IllegalArgumentException("File is a directory " + suggestedFile);
}
final StringBuilder cmap = new StringBuilder();
OutputStream os = null;
try {
os = new FileOutputStream(suggestedFile);
this.exportDiagram(os, cmap, 0, fileFormat);
} finally {
if (os != null) {
os.close();
}
}
List<File> result = Arrays.asList(suggestedFile);
if (this.hasUrl() && cmap.length() > 0) {
exportCmap(suggestedFile, cmap);
}
if (fileFormat.getFileFormat() == FileFormat.PNG) {
result = new PngSplitter(suggestedFile, this.getHorizontalPages(), this.getVerticalPages(), this
.getMetadata(), this.getDpi(fileFormat)).getFiles();
}
return result;
}
@Override
final protected void exportDiagramInternal(OutputStream os, StringBuilder cmap, int index,
FileFormatOption fileFormatOption, List<BufferedImage> flashcodes) throws IOException {
final FileFormat fileFormat = fileFormatOption.getFileFormat();
if (fileFormat == FileFormat.ATXT || fileFormat == FileFormat.UTXT) {
createFilesTxt(os, index, fileFormat);
return;
}
if (fileFormat.name().startsWith("XMI")) {
createFilesXmi(os, fileFormat);
return;
}
//
// if (OptionFlags.getInstance().useJavaInsteadOfDot()) {
// return createPng2(suggestedFile);
// }
if (getUmlDiagramType() == UmlDiagramType.COMPOSITE || (BETA && getUmlDiagramType() == UmlDiagramType.CLASS)) {
final CucaDiagramFileMakerBeta maker = new CucaDiagramFileMakerBeta(this);
try {
maker.createFile(os, getDotStrings(), fileFormat);
} catch (InterruptedException e) {
throw new IOException(e.toString());
}
return;
}
if (getUmlDiagramType() == UmlDiagramType.COMPOSITE) {
final CucaDiagramFileMakerBeta maker = new CucaDiagramFileMakerBeta(this);
try {
@ -325,9 +383,12 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
}
return;
}
final CucaDiagramFileMaker maker = new CucaDiagramFileMaker(this);
final CucaDiagramFileMaker maker = new CucaDiagramFileMaker(this, flashcodes);
try {
maker.createFile(os, getDotStrings(), fileFormatOption);
final String cmapResult = maker.createFile(os, getDotStrings(), fileFormatOption);
if (cmapResult != null && cmap != null) {
cmap.append(cmapResult);
}
} catch (InterruptedException e) {
Log.error(e.toString());
throw new IOException(e.toString());

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6169 $
* Revision $Revision: 6482 $
*
*/
package net.sourceforge.plantuml.cucadiagram;
@ -41,6 +41,7 @@ import java.util.List;
import java.util.Set;
import net.sourceforge.plantuml.UniqueSequence;
import net.sourceforge.plantuml.Url;
import net.sourceforge.plantuml.cucadiagram.dot.DrawFile;
import net.sourceforge.plantuml.graphic.HtmlColor;
import net.sourceforge.plantuml.skin.VisibilityModifier;
@ -62,7 +63,7 @@ public class Entity implements IEntity {
private Group container;
private DrawFile imageFile;
private String url;
private Url url2;
private boolean top;
@ -129,7 +130,7 @@ public class Entity implements IEntity {
}
public List<Member> getMethodsToDisplay() {
if (hides==null || hides.size() == 0) {
if (hides == null || hides.size() == 0) {
return Collections.unmodifiableList(methods);
}
final List<Member> result = new ArrayList<Member>();
@ -142,7 +143,7 @@ public class Entity implements IEntity {
}
public List<Member> getFieldsToDisplay() {
if (hides==null || hides.size() == 0) {
if (hides == null || hides.size() == 0) {
return Collections.unmodifiableList(fields);
}
final List<Member> result = new ArrayList<Member>();
@ -237,13 +238,12 @@ public class Entity implements IEntity {
this.specificBackcolor = HtmlColor.getColorIfValid(s);
}
public final String getUrl() {
return url;
// return "http://www.google.com";
public final Url getUrl() {
return url2;
}
public final void setUrl(String url) {
this.url = url;
public final void setUrl(Url url) {
this.url2 = url;
}
@Override
@ -280,4 +280,15 @@ public class Entity implements IEntity {
}
return null;
}
private boolean nearDecoration = false;
public final boolean hasNearDecoration() {
return nearDecoration;
}
public final void setNearDecoration(boolean nearDecoration) {
this.nearDecoration = nearDecoration;
}
}

View File

@ -37,6 +37,7 @@ import java.io.File;
import java.io.IOException;
import java.util.List;
import net.sourceforge.plantuml.Url;
import net.sourceforge.plantuml.cucadiagram.dot.DrawFile;
import net.sourceforge.plantuml.graphic.HtmlColor;
@ -75,7 +76,7 @@ public abstract class EntityUtils {
return ent.getUid();
}
public String getUrl() {
public Url getUrl() {
return ent.getUrl();
}
@ -126,6 +127,14 @@ public abstract class EntityUtils {
ent.setTop(top);
}
public boolean hasNearDecoration() {
return ent.hasNearDecoration();
}
public void setNearDecoration(boolean nearDecoration) {
ent.setNearDecoration(nearDecoration);
}
};
}

View File

@ -38,6 +38,7 @@ import java.io.IOException;
import java.util.List;
import net.sourceforge.plantuml.SpecificBackcolorable;
import net.sourceforge.plantuml.Url;
import net.sourceforge.plantuml.cucadiagram.dot.DrawFile;
public interface IEntity extends Imaged, SpecificBackcolorable {
@ -50,7 +51,7 @@ public interface IEntity extends Imaged, SpecificBackcolorable {
public String getUid();
public String getUrl();
public Url getUrl();
public List<Member> getFieldsToDisplay();
@ -67,6 +68,9 @@ public interface IEntity extends Imaged, SpecificBackcolorable {
public boolean isTop();
public void setTop(boolean top);
public boolean hasNearDecoration();
public void setNearDecoration(boolean nearDecoration);
}

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6152 $
* Revision $Revision: 6356 $
*
*/
package net.sourceforge.plantuml.cucadiagram;
@ -68,6 +68,7 @@ public class Link implements Imaged {
private final String labelangle;
private HtmlColor specificColor;
private boolean constraint = true;
public Link(IEntity cl1, IEntity cl2, LinkType type, String label, int length) {
this(cl1, cl2, type, label, length, null, null, null, null, null);
@ -96,6 +97,12 @@ public class Link implements Imaged {
this.labeldistance = labeldistance;
this.labelangle = labelangle;
this.specificColor = specificColor;
if (qualifier1 != null) {
cl1.setNearDecoration(true);
}
if (qualifier2 != null) {
cl2.setNearDecoration(true);
}
}
public Link getInv() {
@ -314,4 +321,12 @@ public class Link implements Imaged {
this.specificColor = HtmlColor.getColorIfValid(s);
}
public final boolean isConstraint() {
return constraint;
}
public final void setConstraint(boolean constraint) {
this.constraint = constraint;
}
}

View File

@ -0,0 +1,44 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6295 $
*
*/
package net.sourceforge.plantuml.cucadiagram.dot;
enum BorderMode {
NO_BORDER,
NO_BORDER_CELLSPACING,
BORDER_1_WITH_COLOR,
BORDER_1_WITHOUT_COLOR;
}

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6241 $
* Revision $Revision: 6474 $
*
*/
package net.sourceforge.plantuml.cucadiagram.dot;
@ -47,6 +47,7 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -79,6 +80,7 @@ import net.sourceforge.plantuml.cucadiagram.IEntity;
import net.sourceforge.plantuml.cucadiagram.Imaged;
import net.sourceforge.plantuml.cucadiagram.Link;
import net.sourceforge.plantuml.cucadiagram.Stereotype;
import net.sourceforge.plantuml.eps.EpsGraphics;
import net.sourceforge.plantuml.eps.EpsStrategy;
import net.sourceforge.plantuml.eps.EpsTitler;
import net.sourceforge.plantuml.eps.SvgToEpsConverter;
@ -89,11 +91,11 @@ import net.sourceforge.plantuml.graphic.HtmlColor;
import net.sourceforge.plantuml.graphic.StringBounder;
import net.sourceforge.plantuml.graphic.StringBounderUtils;
import net.sourceforge.plantuml.graphic.VerticalPosition;
import net.sourceforge.plantuml.png.PngFlashcoder;
import net.sourceforge.plantuml.png.PngIO;
import net.sourceforge.plantuml.png.PngRotation;
import net.sourceforge.plantuml.png.PngScaler;
import net.sourceforge.plantuml.png.PngSizer;
import net.sourceforge.plantuml.png.PngSplitter;
import net.sourceforge.plantuml.png.PngTitler;
import net.sourceforge.plantuml.skin.CircleInterface;
import net.sourceforge.plantuml.skin.Component;
@ -110,6 +112,7 @@ import net.sourceforge.plantuml.ugraphic.svg.UGraphicSvg;
public final class CucaDiagramFileMaker {
private final CucaDiagram diagram;
private final List<BufferedImage> flashcodes;
private final StaticFilesMap staticFilesMap;
private final Rose rose = new Rose();
@ -120,9 +123,10 @@ public final class CucaDiagramFileMaker {
stringBounder = StringBounderUtils.asStringBounder(builder.getGraphics2D());
}
public CucaDiagramFileMaker(CucaDiagram diagram) throws IOException {
public CucaDiagramFileMaker(CucaDiagram diagram, List<BufferedImage> flashcodes) throws IOException {
HtmlColor.setForceMonochrome(diagram.getSkinParam().isMonochrome());
this.diagram = diagram;
this.flashcodes = flashcodes;
if (diagram.getUmlDiagramType() == UmlDiagramType.CLASS || diagram.getUmlDiagramType() == UmlDiagramType.OBJECT) {
this.staticFilesMap = new StaticFilesMap(diagram.getSkinParam(), diagram.getDpiFactor(null));
} else {
@ -130,42 +134,6 @@ public final class CucaDiagramFileMaker {
}
}
static String changeName(String name) {
return name.replaceAll("(?i)\\.png$", ".cmapx");
}
public List<File> createFile(File suggested, List<String> dotStrings, FileFormatOption fileFormatOption)
throws IOException, InterruptedException {
final FileFormat fileFormat = fileFormatOption.getFileFormat();
OutputStream os = null;
try {
os = new FileOutputStream(suggested);
final String cmap = createFile(os, dotStrings, fileFormatOption);
if (diagram.hasUrl() && fileFormat == FileFormat.PNG) {
final File cmapFile = new File(changeName(suggested.getAbsolutePath()));
final PrintWriter pw = new PrintWriter(cmapFile);
pw.print(cmap);
pw.close();
}
} finally {
if (os != null) {
os.close();
}
}
if (fileFormat == FileFormat.PNG) {
final List<File> result = new PngSplitter(suggested, diagram.getHorizontalPages(), diagram
.getVerticalPages(), diagram.getMetadata(), diagram.getDpi(fileFormatOption)).getFiles();
for (File f : result) {
Log.info("Creating file: " + f);
}
return result;
}
Log.info("Creating file: " + suggested);
return Arrays.asList(suggested);
}
static private void traceDotString(String dotString) throws IOException {
final File f = new File("dottmpfile" + UniqueSequence.getValue() + ".tmp");
PrintWriter pw = null;
@ -216,28 +184,19 @@ public final class CucaDiagramFileMaker {
private String createSvg(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
throws IOException, InterruptedException {
final StringBuilder cmap = new StringBuilder();
try {
deltaY = 0;
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
final String dotString = dotMaker.createDotString();
if (OptionFlags.getInstance().isKeepTmpFiles()) {
traceDotString(dotString);
}
// final boolean isUnderline = dotMaker.isUnderline();
final Graphviz graphviz = GraphvizUtils.create(dotString, "svg");
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
graphviz.createPng(baos);
baos.close();
dotMaker.clean();
String svg = new String(baos.toByteArray(), "UTF-8").replace('\\', '/');
String svg = getSvgData(dotStrings, fileFormatOption, cmap);
final Dimension2D dim = getDimensionSvg(svg);
if (dim != null) {
svg = removeSvgXmlHeader(svg);
double supH = getTitleSvgHeight();
supH += getHeaderSvgHeight();
supH += getFooterSvgHeight();
svg = removeSvgXmlHeader(svg, dim.getWidth(), dim.getHeight() + supH);
svg = addTitleSvg(svg, dim.getWidth(), dim.getHeight());
svg = addHeaderSvg(svg, dim.getWidth(), dim.getHeight());
@ -304,14 +263,73 @@ public final class CucaDiagramFileMaker {
// cleanTemporaryFiles(diagram.entities().values());
// cleanTemporaryFiles(diagram.getLinks());
}
if (cmap.length() > 0) {
return translateXY(cmap.toString(), 0, (int) Math.round(deltaY));
}
return null;
}
private static String removeSvgXmlHeader(String svg) {
svg = svg
.replaceFirst(
"(?i)<svg[^>]*>",
"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" style=\"width:100%;height:100%;position:absolute;top:0;left:0;\">");
static String translateXY(String cmap, int deltaX, int deltaY) {
if (deltaY == 0) {
return cmap;
}
final Pattern p = Pattern.compile("coords=\"(\\d+),(\\d+),(\\d+),(\\d+)\"");
final Matcher m = p.matcher(cmap);
final StringBuffer sb = new StringBuffer();
while (m.find()) {
final int x1 = Integer.parseInt(m.group(1)) + deltaX;
final int y1 = Integer.parseInt(m.group(2)) + deltaY;
final int x2 = Integer.parseInt(m.group(3)) + deltaX;
final int y2 = Integer.parseInt(m.group(4)) + deltaY;
m.appendReplacement(sb, "coords=\"" + x1 + "," + y1 + "," + x2 + "," + y2 + "\"");
}
m.appendTail(sb);
return sb.toString();
}
private String getSvgData(List<String> dotStrings, FileFormatOption fileFormatOption, StringBuilder cmap)
throws IOException, InterruptedException, UnsupportedEncodingException {
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
final String dotString = dotMaker.createDotString();
if (OptionFlags.getInstance().isKeepTmpFiles()) {
traceDotString(dotString);
}
// if (diagram.hasUrl()) {
// final Graphviz graphviz = GraphvizUtils.create(dotString, "cmapx",
// "svg");
//
// final ByteArrayOutputStream baos = new ByteArrayOutputStream();
// graphviz.createPng(baos);
// baos.close();
// dotMaker.clean();
//
// final String cmapAndSvg = new String(baos.toByteArray(),
// "UTF-8").replace('\\', '/');
// final int x = cmapAndSvg.indexOf("<?xml ");
// if (x == -1) {
// throw new IllegalStateException();
// }
// cmap.append(cmapAndSvg.substring(0, x));
// return cmapAndSvg.substring(x);
//
// }
final Graphviz graphviz = GraphvizUtils.create(dotString, "svg");
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
graphviz.createPng(baos);
baos.close();
dotMaker.clean();
return new String(baos.toByteArray(), "UTF-8").replace('\\', '/');
}
private static String removeSvgXmlHeader(String svg, double width, double height) {
final String newString = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" style=\"width:"
+ Math.round(width) + ";height:" + Math.round(height) + ";\">";
svg = svg.replaceFirst("(?i)<svg[^>]*>", newString);
return svg;
}
@ -412,6 +430,8 @@ public final class CucaDiagramFileMaker {
throws IOException, InterruptedException {
final StringBuilder cmap = new StringBuilder();
double supX = 0;
double supY = 0;
try {
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
final String dotString = dotMaker.createDotString();
@ -448,6 +468,12 @@ public final class CucaDiagramFileMaker {
}
final Color background = diagram.getSkinParam().getBackgroundColor().getColor();
supY = getTitlePngHeight(stringBounder);
supY += getHeaderPngHeight(stringBounder);
supX = getOffsetX(stringBounder, im.getWidth());
im = addTitle(im, background);
im = addFooter(im, background);
im = addHeader(im, background);
@ -457,6 +483,9 @@ public final class CucaDiagramFileMaker {
im = PngRotation.process(im);
}
im = PngSizer.process(im, diagram.getMinwidth());
im = addFlashcode(im, background);
PngIO.write(im, os, diagram.getMetadata(), diagram.getDpi(fileFormatOption));
} finally {
cleanTemporaryFiles(diagram.entities().values());
@ -464,11 +493,18 @@ public final class CucaDiagramFileMaker {
}
if (cmap.length() > 0) {
return cmap.toString();
return translateXY(cmap.toString(), (int) Math.round(supX), (int) Math.round(supY));
}
return null;
}
private BufferedImage addFlashcode(BufferedImage im, Color background) {
if (flashcodes == null) {
return im;
}
return new PngFlashcoder(flashcodes).processImage(im, background);
}
private String createDot(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
throws IOException, InterruptedException {
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
@ -575,6 +611,24 @@ public final class CucaDiagramFileMaker {
return pngTitler.processImage(im, background, 3);
}
private double getTitlePngHeight(StringBounder stringBounder) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
final int fontSize = getSkinParam().getFontSize(FontParam.TITLE, null);
final PngTitler pngTitler = new PngTitler(titleColor, diagram.getTitle(), fontSize, fontFamily,
HorizontalAlignement.CENTER, VerticalPosition.TOP);
return pngTitler.getOffsetY(stringBounder);
}
private double getOffsetX(StringBounder stringBounder, double imWidth) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
final int fontSize = getSkinParam().getFontSize(FontParam.TITLE, null);
final PngTitler pngTitler = new PngTitler(titleColor, diagram.getTitle(), fontSize, fontFamily,
HorizontalAlignement.CENTER, VerticalPosition.TOP);
return pngTitler.getOffsetX(imWidth, stringBounder);
}
private String addTitleSvg(String svg, double width, double height) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
@ -586,6 +640,16 @@ public final class CucaDiagramFileMaker {
return svgTitler.addTitleSvg(svg, width, height);
}
private double getTitleSvgHeight() throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
final int fontSize = getSkinParam().getFontSize(FontParam.TITLE, null);
final SvgTitler svgTitler = new SvgTitler(titleColor, diagram.getTitle(), fontSize, fontFamily,
HorizontalAlignement.CENTER, VerticalPosition.TOP, 3);
return svgTitler.getHeight();
}
private String addTitleEps(String eps) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
@ -616,8 +680,6 @@ public final class CucaDiagramFileMaker {
return epsTitler.addTitleEps(eps);
}
private String addHeaderSvg(String svg, double width, double height) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.HEADER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.HEADER, null);
@ -628,6 +690,15 @@ public final class CucaDiagramFileMaker {
return svgTitler.addTitleSvg(svg, width, height);
}
private double getHeaderSvgHeight() throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.HEADER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.HEADER, null);
final int fontSize = getSkinParam().getFontSize(FontParam.HEADER, null);
final SvgTitler svgTitler = new SvgTitler(titleColor, diagram.getHeader(), fontSize, fontFamily, diagram
.getHeaderAlignement(), VerticalPosition.TOP, 3);
return svgTitler.getHeight();
}
private String addFooterSvg(String svg, double width, double height) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.FOOTER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.FOOTER, null);
@ -637,6 +708,15 @@ public final class CucaDiagramFileMaker {
return svgTitler.addTitleSvg(svg, width, height + deltaY);
}
private double getFooterSvgHeight() throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.FOOTER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.FOOTER, null);
final int fontSize = getSkinParam().getFontSize(FontParam.FOOTER, null);
final SvgTitler svgTitler = new SvgTitler(titleColor, diagram.getFooter(), fontSize, fontFamily, diagram
.getFooterAlignement(), VerticalPosition.BOTTOM, 3);
return svgTitler.getHeight();
}
private BufferedImage addFooter(BufferedImage im, final Color background) {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.FOOTER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.FOOTER, null);
@ -655,6 +735,15 @@ public final class CucaDiagramFileMaker {
return pngTitler.processImage(im, background, 3);
}
private double getHeaderPngHeight(StringBounder stringBounder) throws IOException {
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.HEADER, null).getColor();
final String fontFamily = getSkinParam().getFontFamily(FontParam.HEADER, null);
final int fontSize = getSkinParam().getFontSize(FontParam.HEADER, null);
final PngTitler pngTitler = new PngTitler(titleColor, diagram.getHeader(), fontSize, fontFamily, diagram
.getHeaderAlignement(), VerticalPosition.TOP);
return pngTitler.getOffsetY(stringBounder);
}
private void cleanTemporaryFiles(final Collection<? extends Imaged> imageFiles) throws IOException {
if (OptionFlags.getInstance().isKeepTmpFiles() == false) {
for (Imaged entity : imageFiles) {
@ -842,8 +931,6 @@ public final class CucaDiagramFileMaker {
final Lazy<File> lpng = new Lazy<File>() {
public File getNow() throws IOException {
final EmptyImageBuilder builder = new EmptyImageBuilder(stickMan.getPreferredWidth(null) * dpiFactor,
// stickMan.getPreferredHeight(null) * dpiFactor, dpiFactor > 1
// ? Color.BLUE : background);
stickMan.getPreferredHeight(null) * dpiFactor, background);
final BufferedImage im = builder.getBufferedImage();
@ -920,10 +1007,11 @@ public final class CucaDiagramFileMaker {
final boolean isUnderline = dotMaker.isUnderline();
String eps = new String(baos.toByteArray(), "UTF-8");
eps = cleanStrangeCharacter(eps);
if (isUnderline) {
eps = new UnderlineTrickEps(eps).getString();
}
if (diagram.getTitle() != null) {
eps = addTitleEps(eps);
}
@ -975,7 +1063,6 @@ public final class CucaDiagramFileMaker {
// mImage.appendTail(sb);
// svg = sb.toString();
} finally {
// cleanTemporaryFiles(diagram.entities().values());
// cleanTemporaryFiles(diagram.getLinks());
@ -1041,4 +1128,26 @@ public final class CucaDiagramFileMaker {
}
}
static String cleanStrangeCharacter(String eps) {
final StringBuilder sb = new StringBuilder();
final StringTokenizer st = new StringTokenizer(eps, "\r\n");
while (st.hasMoreTokens()) {
String s = st.nextToken();
if (s.equals(EpsGraphics.END_OF_FILE)) {
sb.append(s);
sb.append("\n");
s = st.nextToken();
if (s.equalsIgnoreCase("grestore") == false) {
s = st.nextToken();
if (s.equalsIgnoreCase("grestore") == false) {
throw new IllegalStateException();
}
}
}
sb.append(s);
sb.append("\n");
}
return sb.toString();
}
}

View File

@ -48,10 +48,10 @@ import java.util.Map;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.cucadiagram.Member;
import net.sourceforge.plantuml.cucadiagram.CucaDiagram;
import net.sourceforge.plantuml.cucadiagram.Entity;
import net.sourceforge.plantuml.cucadiagram.Link;
import net.sourceforge.plantuml.cucadiagram.Member;
import net.sourceforge.plantuml.posimo.Block;
import net.sourceforge.plantuml.posimo.Cluster;
import net.sourceforge.plantuml.posimo.GraphvizSolverB;

View File

@ -0,0 +1,301 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6295 $
*
*/
package net.sourceforge.plantuml.cucadiagram.dot;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import javax.imageio.ImageIO;
import net.sourceforge.plantuml.ColorParam;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FontParam;
import net.sourceforge.plantuml.cucadiagram.EntityPortion;
import net.sourceforge.plantuml.cucadiagram.EntityType;
import net.sourceforge.plantuml.cucadiagram.Group;
import net.sourceforge.plantuml.cucadiagram.IEntity;
import net.sourceforge.plantuml.cucadiagram.Member;
import net.sourceforge.plantuml.cucadiagram.Stereotype;
import net.sourceforge.plantuml.graphic.HtmlColor;
import net.sourceforge.plantuml.skin.rose.Rose;
abstract class DotCommon {
private final DotData data;
private final FileFormat fileFormat;
private boolean underline;
private final Rose rose = new Rose();
DotCommon(FileFormat fileFormat, DotData data) {
this.fileFormat = fileFormat;
this.data = data;
}
protected final Stereotype getStereotype(IEntity entity) {
if (data.showPortion(EntityPortion.STEREOTYPE, entity) == false) {
return null;
}
return entity.getStereotype();
}
protected final boolean isThereLabel(final Stereotype stereotype) {
return stereotype != null && stereotype.getLabel() != null;
}
protected final double getMagicFactorForImageDpi() {
return 10500 / 100000.0;
}
protected final void appendLabelAndStereotype(IEntity entity, final StringBuilder sb, boolean classes) {
final Stereotype stereotype = getStereotype(entity);
final String stereo = entity.getStereotype() == null ? null : entity.getStereotype().getLabel();
if (isThereLabel(stereotype)) {
sb.append("<BR ALIGN=\"LEFT\"/>");
sb.append(manageHtmlIB(stereotype.getLabel(), classes ? FontParam.CLASS_STEREOTYPE
: FontParam.OBJECT_STEREOTYPE, stereo));
sb.append("<BR/>");
}
String display = entity.getDisplay();
final boolean italic = entity.getType() == EntityType.ABSTRACT_CLASS
|| entity.getType() == EntityType.INTERFACE;
if (italic) {
display = "<i>" + display;
}
sb.append(manageHtmlIB(display, classes ? FontParam.CLASS : FontParam.OBJECT, stereo));
}
protected final String manageHtmlIB(String s, FontParam param, String stereotype) {
s = unicode(s);
final int fontSize = data.getSkinParam().getFontSize(param, stereotype);
final int style = data.getSkinParam().getFontStyle(param, stereotype);
final String fontFamily = data.getSkinParam().getFontFamily(param, stereotype);
final DotExpression dotExpression = new DotExpression(s, fontSize, getFontHtmlColor(param, stereotype),
fontFamily, style, fileFormat);
final String result = dotExpression.getDotHtml();
if (dotExpression.isUnderline()) {
underline = true;
}
return result;
}
protected final HtmlColor getFontHtmlColor(FontParam fontParam, String stereotype) {
return data.getSkinParam().getFontHtmlColor(fontParam, stereotype);
}
static String unicode(String s) {
final StringBuilder result = new StringBuilder();
for (char c : s.toCharArray()) {
if (c > 127 || c == '&') {
final int i = c;
result.append("&#" + i + ";");
} else {
result.append(c);
}
}
return result.toString();
}
protected final void addTdImageBugB1983(final StringBuilder sb, final String absolutePath) throws IOException {
// http://www.graphviz.org/bugs/b1983.html
final BufferedImage im = ImageIO.read(new File(absolutePath));
final int height = im.getHeight();
final int width = im.getWidth();
final double f = 1.0 / data.getDpiFactor();
final int w = (int) (width * f);
final int h = (int) (height * f);
final int w2 = (int) (width * getMagicFactorForImageDpi());
final int h2 = (int) (height * getMagicFactorForImageDpi());
sb.append(getTdHeaderForDpi(w, h));
sb.append("<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">");
sb.append("<TR>");
sb.append(getTdHeaderForDpi(w2, h2));
sb.append("<IMG SCALE=\"TRUE\" SRC=\"" + absolutePath + "\"/>");
sb.append("</TD>");
sb.append("</TR>");
sb.append("</TABLE>");
sb.append("</TD>");
}
protected final String getTdHeaderForDpi(final double w, final double h) {
// return "<TD BGCOLOR=\"#000000\" FIXEDSIZE=\"TRUE\" WIDTH=\"" + w +
// "\" HEIGHT=\"" + h + "\">";
return "<TD FIXEDSIZE=\"TRUE\" WIDTH=\"" + w + "\" HEIGHT=\"" + h + "\">";
}
public final boolean isUnderline() {
return underline;
}
protected final DotData getData() {
return data;
}
protected final FileFormat getFileFormat() {
return fileFormat;
}
protected final void setUnderline(boolean underline) {
this.underline = underline;
}
protected final int getLongestMethods(IEntity entity) {
int result = 0;
for (Member att : entity.getMethodsToDisplay()) {
final int size = att.getDisplayWithVisibilityChar().length();
if (size > result) {
result = size;
}
}
return result;
}
protected final int getLongestField(IEntity entity) {
int result = 0;
for (Member att : entity.getFieldsToDisplay()) {
final int size = att.getDisplayWithVisibilityChar().length();
if (size > result) {
result = size;
}
}
return result;
}
protected final String getWitdh55() {
if (getData().getDpi() == 96) {
return "WIDTH=\"55\"";
}
return "WIDTH=\"55\"";
}
protected final int computeSpring(final int current, final int maximum, int maxSpring) {
if (maximum <= current) {
return 0;
}
final int spring = maximum - current;
if (spring > maxSpring) {
return maxSpring;
}
return spring;
}
protected final int getLonguestHeader(IEntity entity) {
int result = entity.getDisplay().length();
final Stereotype stereotype = getStereotype(entity);
if (isThereLabel(stereotype)) {
final int size = stereotype.getLabel().length();
if (size > result) {
result = size;
}
}
return result;
}
protected final String getColorString(ColorParam colorParam, String stereotype) {
return "\"" + rose.getHtmlColor(getData().getSkinParam(), colorParam, stereotype).getAsHtml() + "\"";
}
protected final int getLongestFieldOrAttribute(IEntity entity) {
return Math.max(getLongestField(entity), getLongestMethods(entity));
}
protected final boolean hasStatic(Collection<Member> attributes) {
for (Member att : attributes) {
if (att.isStatic()) {
return true;
}
}
return false;
}
protected final String manageHtmlIBspecial(Member att, FontParam param, boolean hasStatic, String backColor,
boolean withVisibilityChar) {
String prefix = "";
if (hasStatic) {
prefix = "<FONT COLOR=" + backColor + ">_</FONT>";
}
if (att.isAbstract()) {
return prefix + manageHtmlIB("<i>" + att.getDisplay(withVisibilityChar), param, null);
}
if (att.isStatic()) {
return manageHtmlIB("<u>" + att.getDisplay(withVisibilityChar), param, null);
}
return prefix + manageHtmlIB(att.getDisplay(withVisibilityChar), param, null);
}
final protected String getBackColorAroundEntity(IEntity entity) {
String backColor = getSpecificBackColor(entity);
if (backColor == null) {
backColor = getColorString(ColorParam.background, null);
}
return backColor;
}
private String getSpecificBackColor(IEntity entity) {
final Group parent = entity.getParent();
if (parent == null) {
return null;
}
if (parent.getBackColor() == null) {
return null;
}
return "\"" + parent.getBackColor().getAsHtml() + "\"";
}
final protected void appendImageAsTD(StringBuilder sb, String circleAbsolutePath) throws IOException {
if (circleAbsolutePath.endsWith(".png")) {
if (getData().getDpi() == 96) {
final BufferedImage im = ImageIO.read(new File(circleAbsolutePath));
final int height = im.getHeight();
final int width = im.getWidth();
sb.append("<TD FIXEDSIZE=\"TRUE\" WIDTH=\"" + width + "\" HEIGHT=\"" + height + "\"><IMG SRC=\""
+ circleAbsolutePath + "\"/></TD>");
} else {
addTdImageBugB1983(sb, circleAbsolutePath);
}
} else if (circleAbsolutePath.endsWith(".eps")) {
sb.append("<TD><IMG SRC=\"" + circleAbsolutePath + "\"/></TD>");
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,83 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 3977 $
*
*/
package net.sourceforge.plantuml.cucadiagram.dot;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import net.sourceforge.plantuml.EmptyImageBuilder;
import net.sourceforge.plantuml.FileUtils;
import net.sourceforge.plantuml.skin.UDrawable;
import net.sourceforge.plantuml.ugraphic.eps.UGraphicEps;
import net.sourceforge.plantuml.ugraphic.g2d.UGraphicG2d;
public class DrawFileFactory {
public static DrawFile create(final UDrawable drawable, final double width, final double height,
final double dpiFactor, final Color backgground, Object signature) {
final Lazy<File> lpng = new Lazy<File>() {
public File getNow() throws IOException {
final File png = FileUtils.createTempFile("visi", ".png");
final EmptyImageBuilder builder = new EmptyImageBuilder(width * dpiFactor, height * dpiFactor,
backgground);
final BufferedImage im = builder.getBufferedImage();
drawable.drawU(new UGraphicG2d(builder.getGraphics2D(), im, dpiFactor));
ImageIO.write(im, "png", png);
return png;
}
};
final Lazy<File> leps = new Lazy<File>() {
public File getNow() throws IOException {
final File eps = FileUtils.createTempFile("visi", ".eps");
UGraphicEps.copyEpsToFile(drawable, eps);
return eps;
}
};
final Lazy<String> lsvg = new Lazy<String>() {
public String getNow() throws IOException {
return UGraphicG2d.getSvgString(drawable);
}
};
return DrawFile.create(lpng, lsvg, leps, signature);
}
}

View File

@ -28,7 +28,7 @@
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6228 $
* Revision $Revision: 6453 $
*
*/
package net.sourceforge.plantuml.cucadiagram.dot;
@ -47,8 +47,6 @@ import java.util.Map;
import javax.imageio.ImageIO;
import com.sun.org.apache.bcel.internal.generic.GETSTATIC;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FileUtils;
import net.sourceforge.plantuml.ISkinParam;

View File

@ -0,0 +1,44 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6295 $
*
*/
package net.sourceforge.plantuml.cucadiagram.dot;
import java.io.IOException;
interface LabelBuilder {
void appendLabel(StringBuilder sb) throws IOException;
boolean isUnderline();
}

View File

@ -0,0 +1,125 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6295 $
*
*/
package net.sourceforge.plantuml.cucadiagram.dot;
import java.io.IOException;
import net.sourceforge.plantuml.ColorParam;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.FontParam;
import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.cucadiagram.EntityPortion;
import net.sourceforge.plantuml.cucadiagram.IEntity;
import net.sourceforge.plantuml.cucadiagram.Member;
class LabelBuilderClassOld extends LabelBuilderObjectOrClass implements LabelBuilder {
LabelBuilderClassOld(FileFormat fileFormat, DotData data, IEntity entity) {
super(fileFormat, data, entity);
}
public void appendLabel(StringBuilder sb) throws IOException {
DrawFile cFile = getEntity().getImageFile();
if (cFile == null) {
final String stereo = getEntity().getStereotype() == null ? null : getEntity().getStereotype().getLabel();
cFile = getData().getStaticImages(getEntity().getType(), stereo);
}
if (cFile == null) {
throw new IllegalStateException();
}
final String circleAbsolutePath;
if (getData().showPortion(EntityPortion.CIRCLED_CHARACTER, getEntity())) {
circleAbsolutePath = StringUtils.getPlateformDependentAbsolutePath(cFile
.getPngOrEps(getFileFormat() == FileFormat.EPS));
} else {
circleAbsolutePath = null;
}
// sb.append("<");
final boolean showFields = getData().showPortion(EntityPortion.FIELD, getEntity());
final boolean showMethods = getData().showPortion(EntityPortion.METHOD, getEntity());
final String stereo = getEntity().getStereotype() == null ? null : getEntity().getStereotype().getLabel();
if (showFields == false && showMethods == false) {
sb.append(getHtmlHeaderTableForObjectOrClassOrInterfaceOrEnum(getEntity(), circleAbsolutePath, 1, true, BorderMode.NO_BORDER_CELLSPACING));
} else {
sb.append("<TABLE BGCOLOR=" + getColorString(ColorParam.classBackground, stereo) + " COLOR="
+ getColorString(ColorParam.classBorder, stereo)
+ " BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">");
sb.append("<TR><TD>");
final int longuestFieldOrAttribute = getLongestFieldOrAttribute(getEntity());
final int longuestHeader = getLonguestHeader(getEntity());
final int spring = computeSpring(longuestHeader, longuestFieldOrAttribute, 30);
sb.append(getHtmlHeaderTableForObjectOrClassOrInterfaceOrEnum(getEntity(), circleAbsolutePath, spring,
true, BorderMode.NO_BORDER));
sb.append("</TD></TR>");
if (showFields) {
// if (fileFormat == FileFormat.EPS) {
// sb.append(addFieldsEps(getEntity().fields2(), true));
// } else {
final boolean hasStatic = hasStatic(getEntity().getFieldsToDisplay());
sb.append("<TR ALIGN=\"LEFT\"><TD " + getWitdh55() + " ALIGN=\"LEFT\">");
for (Member att : getEntity().getFieldsToDisplay()) {
sb.append(manageHtmlIBspecial(att, FontParam.CLASS_ATTRIBUTE, hasStatic,
getColorString(ColorParam.classBackground, stereo), true));
sb.append("<BR ALIGN=\"LEFT\"/>");
}
sb.append("</TD></TR>");
// }
}
if (showMethods) {
// if (fileFormat == FileFormat.EPS) {
// sb.append(addFieldsEps(getEntity().methods2(), true));
// } else {
final boolean hasStatic = hasStatic(getEntity().getMethodsToDisplay());
sb.append("<TR ALIGN=\"LEFT\"><TD ALIGN=\"LEFT\">");
for (Member att : getEntity().getMethodsToDisplay()) {
sb.append(manageHtmlIBspecial(att, FontParam.CLASS_ATTRIBUTE, hasStatic,
getColorString(ColorParam.classBackground, stereo), true));
sb.append("<BR ALIGN=\"LEFT\"/>");
}
sb.append("</TD></TR>");
// }
}
sb.append("</TABLE>");
}
// sb.append(">");
}
}

View File

@ -0,0 +1,110 @@
/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009, Arnaud Roques
*
* Project Info: http://plantuml.sourceforge.net
*
* This file is part of PlantUML.
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PlantUML distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* Original Author: Arnaud Roques
*
* Revision $Revision: 6295 $
*
*/
package net.sourceforge.plantuml.cucadiagram.dot;
import java.io.IOException;
import net.sourceforge.plantuml.ColorParam;
import net.sourceforge.plantuml.FileFormat;
import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.cucadiagram.EntityPortion;
import net.sourceforge.plantuml.cucadiagram.IEntity;
class LabelBuilderClassWithVisibilityImage extends LabelBuilderObjectOrClass implements LabelBuilder {
LabelBuilderClassWithVisibilityImage(FileFormat fileFormat, DotData data, IEntity entity) {
super(fileFormat, data, entity);
}
public void appendLabel(StringBuilder sb) throws IOException {
DrawFile cFile = getEntity().getImageFile();
if (cFile == null) {
final String stereo = getEntity().getStereotype() == null ? null : getEntity().getStereotype().getLabel();
cFile = getData().getStaticImages(getEntity().getType(), stereo);
}
if (cFile == null) {
throw new IllegalStateException();
}
final String circleAbsolutePath;
if (getData().showPortion(EntityPortion.CIRCLED_CHARACTER, getEntity())) {
circleAbsolutePath = StringUtils.getPlateformDependentAbsolutePath(cFile
.getPngOrEps(getFileFormat() == FileFormat.EPS));
} else {
circleAbsolutePath = null;
}
final boolean showFields = getData().showPortion(EntityPortion.FIELD, getEntity());
final boolean showMethods = getData().showPortion(EntityPortion.METHOD, getEntity());
// sb.append("<");
if (showFields == false && showMethods == false) {
sb.append(getHtmlHeaderTableForObjectOrClassOrInterfaceOrEnum(getEntity(), circleAbsolutePath, 1, true, BorderMode.NO_BORDER_CELLSPACING));
} else {
final String stereo = getEntity().getStereotype() == null ? null : getEntity().getStereotype().getLabel();
final int longuestHeader = getLonguestHeader(getEntity());
final int spring = computeSpring(longuestHeader, getLongestFieldOrAttribute(getEntity()), 30);
final int springField = computeSpring(getLongestField(getEntity()),
Math.max(longuestHeader, getLongestMethods(getEntity())), 30);
final int springMethod = computeSpring(getLongestMethods(getEntity()),
Math.max(longuestHeader, getLongestField(getEntity())), 30);
sb.append("<TABLE BGCOLOR=" + getColorString(ColorParam.classBackground, stereo) + " COLOR="
+ getColorString(ColorParam.classBorder, stereo)
+ " BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" CELLPADDING=\"4\">");
sb.append("<TR><TD>");
sb.append(getHtmlHeaderTableForObjectOrClassOrInterfaceOrEnum(getEntity(), circleAbsolutePath, spring,
true, BorderMode.NO_BORDER));
sb.append("</TD></TR>");
if (showFields) {
sb.append("<TR><TD " + getWitdh55() + ">");
if (getEntity().getFieldsToDisplay().size() > 0) {
buildTableVisibility(getEntity(), true, sb, springField);
}
sb.append("</TD></TR>");
}
if (showMethods) {
sb.append("<TR><TD>");
if (getEntity().getMethodsToDisplay().size() > 0) {
buildTableVisibility(getEntity(), false, sb, springMethod);
}
sb.append("</TD></TR>");
}
sb.append("</TABLE>");
}
// sb.append(">");
}
}

Some files were not shown because too many files have changed in this diff Show More