mirror of
https://github.com/octoleo/plantuml.git
synced 2024-12-22 10:59:01 +00:00
Version 6487
This commit is contained in:
parent
7347fc27af
commit
fe6831d641
103
src/com/google/zxing/BarcodeFormat.java
Normal file
103
src/com/google/zxing/BarcodeFormat.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
src/com/google/zxing/ChecksumException.java
Normal file
37
src/com/google/zxing/ChecksumException.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
81
src/com/google/zxing/DecodeHintType.java
Normal file
81
src/com/google/zxing/DecodeHintType.java
Normal 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() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
src/com/google/zxing/EncodeHintType.java
Normal file
39
src/com/google/zxing/EncodeHintType.java
Normal 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() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
src/com/google/zxing/FormatException.java
Normal file
38
src/com/google/zxing/FormatException.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
src/com/google/zxing/NotFoundException.java
Normal file
37
src/com/google/zxing/NotFoundException.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
98
src/com/google/zxing/ReaderException.java
Normal file
98
src/com/google/zxing/ReaderException.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
127
src/com/google/zxing/ResultPoint.java
Normal file
127
src/com/google/zxing/ResultPoint.java
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
54
src/com/google/zxing/Writer.java
Normal file
54
src/com/google/zxing/Writer.java
Normal 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;
|
||||||
|
|
||||||
|
}
|
35
src/com/google/zxing/WriterException.java
Normal file
35
src/com/google/zxing/WriterException.java
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
80
src/com/google/zxing/client/j2se/MatrixToImageWriter.java
Normal file
80
src/com/google/zxing/client/j2se/MatrixToImageWriter.java
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
247
src/com/google/zxing/common/BitArray.java
Normal file
247
src/com/google/zxing/common/BitArray.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
226
src/com/google/zxing/common/BitMatrix.java
Normal file
226
src/com/google/zxing/common/BitMatrix.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
97
src/com/google/zxing/common/BitSource.java
Normal file
97
src/com/google/zxing/common/BitSource.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
110
src/com/google/zxing/common/CharacterSetECI.java
Normal file
110
src/com/google/zxing/common/CharacterSetECI.java
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
src/com/google/zxing/common/DecoderResult.java
Normal file
63
src/com/google/zxing/common/DecoderResult.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
46
src/com/google/zxing/common/DetectorResult.java
Normal file
46
src/com/google/zxing/common/DetectorResult.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
52
src/com/google/zxing/common/ECI.java
Normal file
52
src/com/google/zxing/common/ECI.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
191
src/com/google/zxing/common/StringUtils.java
Normal file
191
src/com/google/zxing/common/StringUtils.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
139
src/com/google/zxing/common/reedsolomon/GF256.java
Normal file
139
src/com/google/zxing/common/reedsolomon/GF256.java
Normal 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)];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
263
src/com/google/zxing/common/reedsolomon/GF256Poly.java
Normal file
263
src/com/google/zxing/common/reedsolomon/GF256Poly.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
446
src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java
Normal file
446
src/com/google/zxing/datamatrix/decoder/BitMatrixParser.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
118
src/com/google/zxing/datamatrix/decoder/DataBlock.java
Normal file
118
src/com/google/zxing/datamatrix/decoder/DataBlock.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
242
src/com/google/zxing/datamatrix/decoder/Version.java
Normal file
242
src/com/google/zxing/datamatrix/decoder/Version.java
Normal 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)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
106
src/com/google/zxing/qrcode/QRCodeWriter.java
Normal file
106
src/com/google/zxing/qrcode/QRCodeWriter.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
203
src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
Normal file
203
src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
123
src/com/google/zxing/qrcode/decoder/DataBlock.java
Normal file
123
src/com/google/zxing/qrcode/decoder/DataBlock.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
155
src/com/google/zxing/qrcode/decoder/DataMask.java
Normal file
155
src/com/google/zxing/qrcode/decoder/DataMask.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
171
src/com/google/zxing/qrcode/decoder/FormatInformation.java
Normal file
171
src/com/google/zxing/qrcode/decoder/FormatInformation.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
112
src/com/google/zxing/qrcode/decoder/Mode.java
Normal file
112
src/com/google/zxing/qrcode/decoder/Mode.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
586
src/com/google/zxing/qrcode/decoder/Version.java
Normal file
586
src/com/google/zxing/qrcode/decoder/Version.java
Normal 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)))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
src/com/google/zxing/qrcode/encoder/BlockPair.java
Normal file
37
src/com/google/zxing/qrcode/encoder/BlockPair.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
97
src/com/google/zxing/qrcode/encoder/ByteMatrix.java
Normal file
97
src/com/google/zxing/qrcode/encoder/ByteMatrix.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
557
src/com/google/zxing/qrcode/encoder/Encoder.java
Normal file
557
src/com/google/zxing/qrcode/encoder/Encoder.java
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
217
src/com/google/zxing/qrcode/encoder/MaskUtil.java
Normal file
217
src/com/google/zxing/qrcode/encoder/MaskUtil.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
524
src/com/google/zxing/qrcode/encoder/MatrixUtil.java
Normal file
524
src/com/google/zxing/qrcode/encoder/MatrixUtil.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
239
src/com/google/zxing/qrcode/encoder/QRCode.java
Normal file
239
src/com/google/zxing/qrcode/encoder/QRCode.java
Normal 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;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
@ -28,12 +28,18 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6002 $
|
* Revision $Revision: 6302 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml;
|
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.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import net.sourceforge.plantuml.version.Version;
|
import net.sourceforge.plantuml.version.Version;
|
||||||
@ -78,4 +84,22 @@ public abstract class AbstractPSystem implements PSystem {
|
|||||||
return 1;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,15 +45,15 @@ public class BlockUml {
|
|||||||
private final List<String> data;
|
private final List<String> data;
|
||||||
private PSystem system;
|
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) {
|
BlockUml(String... strings) {
|
||||||
this(Arrays.asList(strings));
|
this(Arrays.asList(strings));
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockUml(List<String> strings) {
|
public BlockUml(List<String> strings) {
|
||||||
final String s0 = strings.get(0).trim();
|
final String s0 = strings.get(0).trim();
|
||||||
if (s0.startsWith("@startuml") == false) {
|
if (s0.startsWith("@start") == false) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
this.data = new ArrayList<String>(strings);
|
this.data = new ArrayList<String>(strings);
|
||||||
@ -63,16 +63,23 @@ public class BlockUml {
|
|||||||
if (OptionFlags.getInstance().isWord()) {
|
if (OptionFlags.getInstance().isWord()) {
|
||||||
return null;
|
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();
|
final boolean ok = m.find();
|
||||||
if (ok == false) {
|
if (ok == false) {
|
||||||
return null;
|
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 {
|
public PSystem getSystem() throws IOException, InterruptedException {
|
||||||
if (system==null) {
|
if (system == null) {
|
||||||
createSystem();
|
createSystem();
|
||||||
}
|
}
|
||||||
return system;
|
return system;
|
||||||
@ -80,7 +87,7 @@ public class BlockUml {
|
|||||||
|
|
||||||
private void createSystem() throws IOException, InterruptedException {
|
private void createSystem() throws IOException, InterruptedException {
|
||||||
system = new PSystemBuilder().createPSystem(data);
|
system = new PSystemBuilder().createPSystem(data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
private void init(Preprocessor includer, List<String> config) throws IOException {
|
||||||
String s = null;
|
String s = null;
|
||||||
List<String> current = null;
|
List<String> current = null;
|
||||||
while ((s = includer.readLine()) != null) {
|
while ((s = includer.readLine()) != null) {
|
||||||
if (isArobaseStartuml(s)) {
|
if (StartUtils.isArobaseStartDiagram(s)) {
|
||||||
current = new ArrayList<String>();
|
current = new ArrayList<String>();
|
||||||
}
|
}
|
||||||
if (current != null) {
|
if (current != null) {
|
||||||
current.add(s);
|
current.add(s);
|
||||||
}
|
}
|
||||||
if (isArobaseEnduml(s) && current != null) {
|
if (StartUtils.isArobaseEndDiagram(s) && current != null) {
|
||||||
current.addAll(1, config);
|
current.addAll(1, config);
|
||||||
blocks.add(new BlockUml(current));
|
blocks.add(new BlockUml(current));
|
||||||
current = null;
|
current = null;
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 5823 $
|
* Revision $Revision: 6475 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml;
|
package net.sourceforge.plantuml;
|
||||||
@ -82,15 +82,17 @@ public enum ColorParam {
|
|||||||
|
|
||||||
sequenceActorBackground(true),
|
sequenceActorBackground(true),
|
||||||
sequenceActorBorder,
|
sequenceActorBorder,
|
||||||
|
sequenceGroupBorder,
|
||||||
sequenceGroupBackground(true),
|
sequenceGroupBackground(true),
|
||||||
|
sequenceReferenceBackground(true),
|
||||||
sequenceDividerBackground(true),
|
sequenceDividerBackground(true),
|
||||||
sequenceLifeLineBackground(true),
|
sequenceLifeLineBackground(true),
|
||||||
sequenceLifeLineBorder,
|
sequenceLifeLineBorder,
|
||||||
sequenceParticipantBackground(true),
|
sequenceParticipantBackground(true),
|
||||||
sequenceParticipantBorder,
|
sequenceParticipantBorder,
|
||||||
sequenceArrow,
|
sequenceArrow,
|
||||||
sequenceEngloberLine,
|
sequenceBoxBorder,
|
||||||
sequenceEngloberBackground(true),
|
sequenceBoxBackground(true),
|
||||||
|
|
||||||
iconPrivate,
|
iconPrivate,
|
||||||
iconPrivateBackground,
|
iconPrivateBackground,
|
||||||
|
54
src/net/sourceforge/plantuml/DiagramType.java
Normal file
54
src/net/sourceforge/plantuml/DiagramType.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
63
src/net/sourceforge/plantuml/EmbededDiagram.java
Normal file
63
src/net/sourceforge/plantuml/EmbededDiagram.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 5942 $
|
* Revision $Revision: 6475 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml;
|
package net.sourceforge.plantuml;
|
||||||
@ -55,11 +55,12 @@ public enum FontParam {
|
|||||||
PACKAGE(14, Font.PLAIN, "black", null),
|
PACKAGE(14, Font.PLAIN, "black", null),
|
||||||
SEQUENCE_ACTOR(13, Font.PLAIN, "black", null),
|
SEQUENCE_ACTOR(13, Font.PLAIN, "black", null),
|
||||||
SEQUENCE_ARROW(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_DIVIDER(13, Font.BOLD, "black", null),
|
||||||
|
SEQUENCE_REFERENCE(13, Font.PLAIN, "black", null),
|
||||||
SEQUENCE_DELAY(11, Font.PLAIN, "black", null),
|
SEQUENCE_DELAY(11, Font.PLAIN, "black", null),
|
||||||
SEQUENCE_GROUPING(11, Font.BOLD, "black", null),
|
SEQUENCE_GROUP(11, Font.BOLD, "black", null),
|
||||||
SEQUENCE_GROUPING_HEADER(13, Font.BOLD, "black", null),
|
SEQUENCE_GROUP_HEADER(13, Font.BOLD, "black", null),
|
||||||
SEQUENCE_PARTICIPANT(13, Font.PLAIN, "black", null),
|
SEQUENCE_PARTICIPANT(13, Font.PLAIN, "black", null),
|
||||||
SEQUENCE_TITLE(13, Font.BOLD, "black", null),
|
SEQUENCE_TITLE(13, Font.BOLD, "black", null),
|
||||||
STATE(14, Font.PLAIN, "black", null),
|
STATE(14, Font.PLAIN, "black", null),
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6187 $
|
* Revision $Revision: 6448 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml;
|
package net.sourceforge.plantuml;
|
||||||
@ -172,6 +172,8 @@ public class Option {
|
|||||||
OptionPrint.printVersion();
|
OptionPrint.printVersion();
|
||||||
} else if (s.startsWith("-D")) {
|
} else if (s.startsWith("-D")) {
|
||||||
manageDefine(s.substring(2));
|
manageDefine(s.substring(2));
|
||||||
|
} else if (s.startsWith("-S")) {
|
||||||
|
manageSkinParam(s.substring(2));
|
||||||
} else if (s.equalsIgnoreCase("-testdot")) {
|
} else if (s.equalsIgnoreCase("-testdot")) {
|
||||||
OptionPrint.printTestDot();
|
OptionPrint.printTestDot();
|
||||||
} else if (s.equalsIgnoreCase("-about") || s.equalsIgnoreCase("-author") || s.equalsIgnoreCase("-authors")) {
|
} 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() {
|
public final File getOutputDir() {
|
||||||
return outputDir;
|
return outputDir;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6007 $
|
* Revision $Revision: 6448 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml;
|
package net.sourceforge.plantuml;
|
||||||
@ -56,7 +56,7 @@ public class OptionPrint {
|
|||||||
|
|
||||||
System.err.println("Usage: java -jar plantuml.jar [options] -gui");
|
System.err.println("Usage: java -jar plantuml.jar [options] -gui");
|
||||||
System.err.println("\t(to execute the 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("\t(to process files or directories)");
|
||||||
System.err.println();
|
System.err.println();
|
||||||
System.err.println("You can use the following wildcards in files/dirs:");
|
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("where options include:");
|
||||||
System.err.println(" -gui\t\tTo run the graphical user interface");
|
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(" -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(" -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(" -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(" -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");
|
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(" -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(" -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(" -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();
|
||||||
System.err.println("If needed, you can setup the environment variable GRAPHVIZ_DOT.");
|
System.err.println("If needed, you can setup the environment variable GRAPHVIZ_DOT.");
|
||||||
exit();
|
exit();
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 5794 $
|
* Revision $Revision: 6302 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml;
|
package net.sourceforge.plantuml;
|
||||||
@ -40,9 +40,9 @@ import java.util.List;
|
|||||||
|
|
||||||
public interface PSystem {
|
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();
|
int getNbImages();
|
||||||
|
|
||||||
|
@ -43,6 +43,8 @@ import net.sourceforge.plantuml.activitydiagram2.ActivityDiagramFactory2;
|
|||||||
import net.sourceforge.plantuml.classdiagram.ClassDiagramFactory;
|
import net.sourceforge.plantuml.classdiagram.ClassDiagramFactory;
|
||||||
import net.sourceforge.plantuml.componentdiagram.ComponentDiagramFactory;
|
import net.sourceforge.plantuml.componentdiagram.ComponentDiagramFactory;
|
||||||
import net.sourceforge.plantuml.compositediagram.CompositeDiagramFactory;
|
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.PSystemEggFactory;
|
||||||
import net.sourceforge.plantuml.eggs.PSystemLostFactory;
|
import net.sourceforge.plantuml.eggs.PSystemLostFactory;
|
||||||
import net.sourceforge.plantuml.eggs.PSystemPathFactory;
|
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.oregon.PSystemOregonFactory;
|
||||||
import net.sourceforge.plantuml.postit.PostIdDiagramFactory;
|
import net.sourceforge.plantuml.postit.PostIdDiagramFactory;
|
||||||
import net.sourceforge.plantuml.printskin.PrintSkinFactory;
|
import net.sourceforge.plantuml.printskin.PrintSkinFactory;
|
||||||
|
import net.sourceforge.plantuml.project.PSystemProjectFactory;
|
||||||
import net.sourceforge.plantuml.sequencediagram.SequenceDiagramFactory;
|
import net.sourceforge.plantuml.sequencediagram.SequenceDiagramFactory;
|
||||||
import net.sourceforge.plantuml.statediagram.StateDiagramFactory;
|
import net.sourceforge.plantuml.statediagram.StateDiagramFactory;
|
||||||
import net.sourceforge.plantuml.sudoku.PSystemSudokuFactory;
|
import net.sourceforge.plantuml.sudoku.PSystemSudokuFactory;
|
||||||
@ -75,16 +78,26 @@ public class PSystemBuilder {
|
|||||||
factories.add(new PostIdDiagramFactory());
|
factories.add(new PostIdDiagramFactory());
|
||||||
factories.add(new PrintSkinFactory());
|
factories.add(new PrintSkinFactory());
|
||||||
factories.add(new PSystemVersionFactory());
|
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 PSystemSudokuFactory());
|
||||||
factories.add(new PSystemEggFactory());
|
factories.add(new PSystemEggFactory());
|
||||||
factories.add(new PSystemRIPFactory());
|
factories.add(new PSystemRIPFactory());
|
||||||
factories.add(new PSystemLostFactory());
|
factories.add(new PSystemLostFactory());
|
||||||
factories.add(new PSystemPathFactory());
|
factories.add(new PSystemPathFactory());
|
||||||
factories.add(new PSystemOregonFactory());
|
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>();
|
final List<PSystemError> errors = new ArrayList<PSystemError>();
|
||||||
for (PSystemFactory systemFactory : factories) {
|
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)) {
|
if (isOk(sys)) {
|
||||||
return sys;
|
return sys;
|
||||||
}
|
}
|
||||||
|
@ -28,17 +28,14 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 5999 $
|
* Revision $Revision: 6453 $
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml;
|
package net.sourceforge.plantuml;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
@ -79,24 +76,24 @@ public class PSystemError extends AbstractPSystem {
|
|||||||
this(source, Collections.singletonList(singleError));
|
this(source, Collections.singletonList(singleError));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<File> createFiles(File suggestedFile, FileFormatOption fileFormat) throws IOException,
|
// public List<File> createFiles(File suggestedFile, FileFormatOption fileFormat) throws IOException,
|
||||||
InterruptedException {
|
// InterruptedException {
|
||||||
if (suggestedFile.exists() && suggestedFile.isDirectory()) {
|
// if (suggestedFile.exists() && suggestedFile.isDirectory()) {
|
||||||
throw new IllegalArgumentException("File is a directory " + suggestedFile);
|
// throw new IllegalArgumentException("File is a directory " + suggestedFile);
|
||||||
}
|
// }
|
||||||
OutputStream os = null;
|
// OutputStream os = null;
|
||||||
try {
|
// try {
|
||||||
os = new FileOutputStream(suggestedFile);
|
// os = new FileOutputStream(suggestedFile);
|
||||||
getPngError().writeImage(os, getMetadata(), fileFormat);
|
// getPngError().writeImage(os, getMetadata(), fileFormat);
|
||||||
} finally {
|
// } finally {
|
||||||
if (os != null) {
|
// if (os != null) {
|
||||||
os.close();
|
// os.close();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return Arrays.asList(suggestedFile);
|
// 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);
|
getPngError().writeImage(os, getMetadata(), fileFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 3824 $
|
* Revision $Revision: 6341 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml;
|
package net.sourceforge.plantuml;
|
||||||
@ -38,5 +38,7 @@ public interface PSystemFactory {
|
|||||||
PSystem getSystem();
|
PSystem getSystem();
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
DiagramType getDiagramType();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ final public class PSystemSingleBuilder {
|
|||||||
public PSystemSingleBuilder(UmlSource s, PSystemFactory systemFactory) throws IOException {
|
public PSystemSingleBuilder(UmlSource s, PSystemFactory systemFactory) throws IOException {
|
||||||
this.source = s;
|
this.source = s;
|
||||||
it = s.iterator();
|
it = s.iterator();
|
||||||
if (BlockUmlBuilder.isArobaseStartuml(next()) == false) {
|
if (StartUtils.isArobaseStartDiagram(next()) == false) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ final public class PSystemSingleBuilder {
|
|||||||
systemFactory.reset();
|
systemFactory.reset();
|
||||||
while (hasNext()) {
|
while (hasNext()) {
|
||||||
final String s = next();
|
final String s = next();
|
||||||
if (BlockUmlBuilder.isArobaseEnduml(s)) {
|
if (StartUtils.isArobaseEndDiagram(s)) {
|
||||||
if (source.getSize() == 2) {
|
if (source.getSize() == 2) {
|
||||||
assert false;
|
assert false;
|
||||||
sys = buildEmptyError();
|
sys = buildEmptyError();
|
||||||
@ -130,7 +130,7 @@ final public class PSystemSingleBuilder {
|
|||||||
systemFactory.reset();
|
systemFactory.reset();
|
||||||
while (hasNext()) {
|
while (hasNext()) {
|
||||||
final String s = next();
|
final String s = next();
|
||||||
if (BlockUmlBuilder.isArobaseEnduml(s)) {
|
if (StartUtils.isArobaseEndDiagram(s)) {
|
||||||
final String err = ((AbstractUmlSystemCommandFactory) systemFactory).checkFinalError();
|
final String err = ((AbstractUmlSystemCommandFactory) systemFactory).checkFinalError();
|
||||||
if (err != null) {
|
if (err != null) {
|
||||||
sys = buildEmptyError(err);
|
sys = buildEmptyError(err);
|
||||||
@ -201,7 +201,7 @@ final public class PSystemSingleBuilder {
|
|||||||
lines.add(init);
|
lines.add(init);
|
||||||
while (hasNext()) {
|
while (hasNext()) {
|
||||||
final String s = next();
|
final String s = next();
|
||||||
if (BlockUmlBuilder.isArobaseEnduml(s)) {
|
if (StartUtils.isArobaseEndDiagram(s)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
lines.add(s);
|
lines.add(s);
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6228 $
|
* Revision $Revision: 6448 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml;
|
package net.sourceforge.plantuml;
|
||||||
@ -240,7 +240,7 @@ public class SkinParam implements ISkinParam {
|
|||||||
public static Collection<String> getPossibleValues() {
|
public static Collection<String> getPossibleValues() {
|
||||||
final Set<String> result = new TreeSet<String>();
|
final Set<String> result = new TreeSet<String>();
|
||||||
result.add("Monochrome");
|
result.add("Monochrome");
|
||||||
result.add("BackgroundColor");
|
// result.add("BackgroundColor");
|
||||||
result.add("CircledCharacterRadius");
|
result.add("CircledCharacterRadius");
|
||||||
result.add("ClassAttributeIconSize");
|
result.add("ClassAttributeIconSize");
|
||||||
result.add("DefaultFontName");
|
result.add("DefaultFontName");
|
||||||
@ -254,9 +254,17 @@ public class SkinParam implements ISkinParam {
|
|||||||
result.add(h + "FontSize");
|
result.add(h + "FontSize");
|
||||||
result.add(h + "FontColor");
|
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);
|
return Collections.unmodifiableSet(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String capitalize(String name) {
|
||||||
|
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
public int getDpi() {
|
public int getDpi() {
|
||||||
final String value = getValue("dpi");
|
final String value = getValue("dpi");
|
||||||
if (value != null && value.matches("\\d+")) {
|
if (value != null && value.matches("\\d+")) {
|
||||||
@ -286,8 +294,7 @@ public class SkinParam implements ISkinParam {
|
|||||||
}
|
}
|
||||||
return DotSplines.SPLINES;
|
return DotSplines.SPLINES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public GraphvizLayoutStrategy getStrategy() {
|
public GraphvizLayoutStrategy getStrategy() {
|
||||||
final String value = getValue("layout");
|
final String value = getValue("layout");
|
||||||
if ("neato".equalsIgnoreCase(value)) {
|
if ("neato".equalsIgnoreCase(value)) {
|
||||||
@ -305,5 +312,4 @@ public class SkinParam implements ISkinParam {
|
|||||||
return GraphvizLayoutStrategy.DOT;
|
return GraphvizLayoutStrategy.DOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ public class SourceFileReader {
|
|||||||
final File suggested = new File(outputDirectory, newName);
|
final File suggested = new File(outputDirectory, newName);
|
||||||
suggested.getParentFile().mkdirs();
|
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 String desc = "[" + file.getName() + "] " + blockUml.getSystem().getDescription();
|
||||||
final GeneratedImage generatedImage = new GeneratedImage(f, desc);
|
final GeneratedImage generatedImage = new GeneratedImage(f, desc);
|
||||||
result.add(generatedImage);
|
result.add(generatedImage);
|
||||||
|
@ -92,7 +92,7 @@ public class SourceStringReader {
|
|||||||
final PSystem system = b.getSystem();
|
final PSystem system = b.getSystem();
|
||||||
final int nbInSystem = system.getNbImages();
|
final int nbInSystem = system.getNbImages();
|
||||||
if (numImage < nbInSystem) {
|
if (numImage < nbInSystem) {
|
||||||
system.createFile(os, numImage, fileFormatOption);
|
system.exportDiagram(os, null, numImage, fileFormatOption);
|
||||||
return system.getDescription();
|
return system.getDescription();
|
||||||
}
|
}
|
||||||
numImage -= nbInSystem;
|
numImage -= nbInSystem;
|
||||||
|
49
src/net/sourceforge/plantuml/StartUtils.java
Normal file
49
src/net/sourceforge/plantuml/StartUtils.java
Normal 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,15 +28,35 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6097 $
|
* Revision $Revision: 6382 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml;
|
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 java.util.List;
|
||||||
|
|
||||||
|
import net.sourceforge.plantuml.code.Compression;
|
||||||
|
import net.sourceforge.plantuml.code.CompressionZlib;
|
||||||
import net.sourceforge.plantuml.graphic.HorizontalAlignement;
|
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 {
|
public abstract class UmlDiagram extends AbstractPSystem implements PSystem {
|
||||||
|
|
||||||
private boolean rotation;
|
private boolean rotation;
|
||||||
@ -151,4 +171,134 @@ public abstract class UmlDiagram extends AbstractPSystem implements PSystem {
|
|||||||
this.hideUnlinkedData = hideUnlinkedData;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,37 +33,27 @@
|
|||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml;
|
package net.sourceforge.plantuml;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
final public class UmlSource {
|
final public class UmlSource {
|
||||||
|
|
||||||
final private List<String> source = new ArrayList<String>();
|
final private List<String> source;
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public UmlSource(UmlSource start) {
|
|
||||||
this.source.addAll(start.source);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UmlSource(List<String> source) {
|
public UmlSource(List<String> source) {
|
||||||
this.source.addAll(source);
|
this.source = Collections.unmodifiableList(new ArrayList<String>(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
public DiagramType getDiagramType() {
|
||||||
public UmlSource() {
|
return DiagramType.getTypeFromArobaseStart(source.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterator<String> iterator() {
|
public Iterator<String> iterator() {
|
||||||
return source.iterator();
|
return source.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public void append(String s) {
|
|
||||||
source.add(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPlainString() {
|
public String getPlainString() {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
for (String s : source) {
|
for (String s : source) {
|
||||||
@ -83,10 +73,10 @@ final public class UmlSource {
|
|||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
for (String s : source) {
|
for (String s : source) {
|
||||||
if (BlockUmlBuilder.isArobaseStartuml(s)) {
|
if (StartUtils.isArobaseStartDiagram(s)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (BlockUmlBuilder.isArobaseEnduml(s)) {
|
if (StartUtils.isArobaseEndDiagram(s)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (s.matches("\\s*'.*")) {
|
if (s.matches("\\s*'.*")) {
|
||||||
|
63
src/net/sourceforge/plantuml/Url.java
Normal file
63
src/net/sourceforge/plantuml/Url.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -70,7 +70,7 @@ public class CommandLinkActivity extends SingleLineCommand2<ActivityDiagram> {
|
|||||||
new RegexLeaf("\\s*"), //
|
new RegexLeaf("\\s*"), //
|
||||||
new RegexLeaf("BACKCOLOR", "(#\\w+)?"), //
|
new RegexLeaf("BACKCOLOR", "(#\\w+)?"), //
|
||||||
new RegexLeaf("\\s*"), //
|
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("\\s*"), //
|
||||||
new RegexLeaf("BRACKET", "(?:\\[([^\\]*]+[^\\]]*)\\])?"), //
|
new RegexLeaf("BRACKET", "(?:\\[([^\\]*]+[^\\]]*)\\])?"), //
|
||||||
new RegexLeaf("\\s*"), //
|
new RegexLeaf("\\s*"), //
|
||||||
@ -114,9 +114,15 @@ public class CommandLinkActivity extends SingleLineCommand2<ActivityDiagram> {
|
|||||||
final String linkLabel = arg2.get("BRACKET").get(0);
|
final String linkLabel = arg2.get("BRACKET").get(0);
|
||||||
|
|
||||||
final String arrow = StringUtils.manageArrowForCuca(arg2.get("ARROW").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);
|
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));
|
final Direction direction = StringUtils.getArrowDirection(arg2.get("ARROW").get(0));
|
||||||
if (direction == Direction.LEFT || direction == Direction.UP) {
|
if (direction == Direction.LEFT || direction == Direction.UP) {
|
||||||
link = link.getInv();
|
link = link.getInv();
|
||||||
|
@ -53,12 +53,13 @@ import net.sourceforge.plantuml.cucadiagram.IEntity;
|
|||||||
import net.sourceforge.plantuml.cucadiagram.Link;
|
import net.sourceforge.plantuml.cucadiagram.Link;
|
||||||
import net.sourceforge.plantuml.cucadiagram.LinkDecor;
|
import net.sourceforge.plantuml.cucadiagram.LinkDecor;
|
||||||
import net.sourceforge.plantuml.cucadiagram.LinkType;
|
import net.sourceforge.plantuml.cucadiagram.LinkType;
|
||||||
|
import net.sourceforge.plantuml.cucadiagram.dot.DotMaker;
|
||||||
|
|
||||||
public class ActivityDiagram2 extends CucaDiagram {
|
public class ActivityDiagram2 extends CucaDiagram {
|
||||||
|
|
||||||
private Collection<IEntity> waitings = new LinkedHashSet<IEntity>();
|
private Collection<IEntity> waitings = new LinkedHashSet<IEntity>();
|
||||||
private ConditionalContext2 currentContext;
|
private ConditionalContext2 currentContext;
|
||||||
private int futureLength = 2;
|
// private int futureLength = 2;
|
||||||
private String futureLabel = null;
|
private String futureLabel = null;
|
||||||
|
|
||||||
private final Collection<String> pendingLabels = new HashSet<String>();
|
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) {
|
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) {
|
if (waitings.size() == 0) {
|
||||||
// throw new IllegalStateException(bar);
|
// throw new IllegalStateException(bar);
|
||||||
}
|
}
|
||||||
label(bar);
|
label(bar);
|
||||||
final Entity act = createEntity(getAutoCode(), bar, EntityType.SYNCHRO_BAR);
|
final Entity act = createEntity(getAutoCode(), bar, EntityType.SYNCHRO_BAR);
|
||||||
|
bars.put(bar, act);
|
||||||
afterAdd(act);
|
afterAdd(act);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void afterAdd(final IEntity act) {
|
private void afterAdd(final IEntity dest) {
|
||||||
for (IEntity last : this.waitings) {
|
for (IEntity last : this.waitings) {
|
||||||
// System.err.println("last=" + last);
|
// System.err.println("last=" + last);
|
||||||
// System.err.println("act=" + act);
|
// 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;
|
futureLabel = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String p : pendingLabels) {
|
for (String p : pendingLabels) {
|
||||||
labels.put(p, act);
|
labels.put(p, dest);
|
||||||
}
|
}
|
||||||
pendingLabels.clear();
|
pendingLabels.clear();
|
||||||
|
|
||||||
this.waitings.clear();
|
this.waitings.clear();
|
||||||
this.waitings.add(act);
|
this.waitings.add(dest);
|
||||||
this.futureLength = 2;
|
// this.futureLength = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEntity getLastEntityConsulted() {
|
public IEntity getLastEntityConsulted() {
|
||||||
@ -137,22 +153,25 @@ public class ActivityDiagram2 extends CucaDiagram {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startIf(String test, String when) {
|
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);
|
currentContext = new ConditionalContext2(currentContext, br, Direction.DOWN, when);
|
||||||
for (IEntity last : this.waitings) {
|
for (IEntity last : this.waitings) {
|
||||||
if (test == null) {
|
// if (test == null) {
|
||||||
// this.addLink(new Link(last, br, new LinkType(LinkDecor.ARROW,
|
// // this.addLink(new Link(last, br, new LinkType(LinkDecor.ARROW,
|
||||||
// LinkDecor.NONE), test, futureLength));
|
// // LinkDecor.NONE), test, futureLength));
|
||||||
throw new IllegalArgumentException();
|
// throw new IllegalArgumentException();
|
||||||
} else {
|
// } else {
|
||||||
this.addLink(new Link(last, br, new LinkType(LinkDecor.ARROW, LinkDecor.NONE), this.futureLabel,
|
this.addLink(new Link(last, br, new LinkType(LinkDecor.ARROW, LinkDecor.NONE), this.futureLabel, 2, null,
|
||||||
futureLength, null, test, getLabeldistance(), getLabelangle()));
|
test, getLabeldistance(), getLabelangle()));
|
||||||
}
|
// }
|
||||||
test = null;
|
test = null;
|
||||||
}
|
}
|
||||||
this.waitings.clear();
|
this.waitings.clear();
|
||||||
this.waitings.add(br);
|
this.waitings.add(br);
|
||||||
this.futureLength = 2;
|
// this.futureLength = 2;
|
||||||
this.futureLabel = when;
|
this.futureLabel = when;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,15 +180,15 @@ public class ActivityDiagram2 extends CucaDiagram {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
public void endif() {
|
public void endif() {
|
||||||
final boolean hasElse = currentContext.isHasElse();
|
// final boolean hasElse = currentContext.isHasElse();
|
||||||
// System.err.println("CALL endif hasElse " + hasElse);
|
// System.err.println("CALL endif hasElse " + hasElse);
|
||||||
this.waitings.addAll(currentContext.getPendings());
|
this.waitings.addAll(currentContext.getPendings());
|
||||||
currentContext = currentContext.getParent();
|
currentContext = currentContext.getParent();
|
||||||
// if (currentContext == null) {
|
// if (currentContext == null) {
|
||||||
// System.err.println("after endif " + currentContext);
|
// System.err.println("after endif " + currentContext);
|
||||||
// } else {
|
// } else {
|
||||||
// System.err.println("after endif " + currentContext.getPendings());
|
// System.err.println("after endif " + currentContext.getPendings());
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void else2(String when) {
|
public void else2(String when) {
|
||||||
@ -187,7 +206,10 @@ public class ActivityDiagram2 extends CucaDiagram {
|
|||||||
if (pending.getLinkLabel() != null) {
|
if (pending.getLinkLabel() != null) {
|
||||||
this.futureLabel = pending.getLinkLabel();
|
this.futureLabel = pending.getLinkLabel();
|
||||||
}
|
}
|
||||||
|
final List<IEntity> olds = new ArrayList<IEntity>(waitings);
|
||||||
|
waitings.clear();
|
||||||
waitings.add(pending.getEntityFrom());
|
waitings.add(pending.getEntityFrom());
|
||||||
|
waitings.addAll(olds);
|
||||||
it.remove();
|
it.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,10 +224,16 @@ public class ActivityDiagram2 extends CucaDiagram {
|
|||||||
if (dest == null) {
|
if (dest == null) {
|
||||||
this.pendingLinks.add(new PendingLink(last, gotoLabel, this.futureLabel));
|
this.pendingLinks.add(new PendingLink(last, gotoLabel, this.futureLabel));
|
||||||
} else {
|
} else {
|
||||||
this.addLink(new Link(last, dest, new LinkType(LinkDecor.ARROW, LinkDecor.NONE), this.futureLabel,
|
// final Link link = new Link(last, dest, new LinkType(LinkDecor.ARROW, LinkDecor.NONE),
|
||||||
this.futureLength));
|
// 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);
|
// System.err.println("Avant fin goto, waitings=" + waitings);
|
||||||
this.waitings.clear();
|
this.waitings.clear();
|
||||||
// currentContext.clearPendingsButFirst();
|
// currentContext.clearPendingsButFirst();
|
||||||
|
@ -78,7 +78,7 @@ public class ConditionalContext2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void clearPendingsButFirst() {
|
public void clearPendingsButFirst() {
|
||||||
System.err.println("ConditionalContext2::clearPendingsButFirst");
|
//System.err.println("ConditionalContext2::clearPendingsButFirst");
|
||||||
this.pendings.clear();
|
this.pendings.clear();
|
||||||
pendings.add(branch);
|
pendings.add(branch);
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ public class ConditionalContext2 {
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
this.hasElse = true;
|
this.hasElse = true;
|
||||||
System.err.println("pend=" + pendings);
|
//System.err.println("pend=" + pendings);
|
||||||
if (pendings.size() == 0) {
|
if (pendings.size() == 0) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
@ -59,4 +59,9 @@ public class PendingLink {
|
|||||||
return linkLabel;
|
return linkLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return entityFrom + " -> " + gotoLabel + " " + linkLabel;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ import net.sourceforge.plantuml.command.SingleLineCommand;
|
|||||||
public class CommandNewActivity2 extends SingleLineCommand<ActivityDiagram2> {
|
public class CommandNewActivity2 extends SingleLineCommand<ActivityDiagram2> {
|
||||||
|
|
||||||
public CommandNewActivity2(ActivityDiagram2 diagram) {
|
public CommandNewActivity2(ActivityDiagram2 diagram) {
|
||||||
super(diagram, "(?i)^[\"<](.+)[\">]$");
|
super(diagram, "(?i)^\\s*[-*]\\s*([^\"\\s].*)$");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,7 +43,7 @@ import net.sourceforge.plantuml.command.CommandMultilines;
|
|||||||
public class CommandNewMultilinesActivity2 extends CommandMultilines<ActivityDiagram2> {
|
public class CommandNewMultilinesActivity2 extends CommandMultilines<ActivityDiagram2> {
|
||||||
|
|
||||||
public CommandNewMultilinesActivity2(final ActivityDiagram2 system) {
|
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) {
|
public final CommandExecutionResult execute(List<String> lines) {
|
||||||
@ -55,6 +55,12 @@ public class CommandNewMultilinesActivity2 extends CommandMultilines<ActivityDia
|
|||||||
return CommandExecutionResult.error("Unreachable statement");
|
return CommandExecutionResult.error("Unreachable statement");
|
||||||
}
|
}
|
||||||
String s = StringUtils.getMergedLines(lines);
|
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);
|
s = s.substring(1, s.length() - 2);
|
||||||
|
|
||||||
getSystem().newActivity(s);
|
getSystem().newActivity(s);
|
||||||
|
42
src/net/sourceforge/plantuml/braille/BrailleUtils.java
Normal file
42
src/net/sourceforge/plantuml/braille/BrailleUtils.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6169 $
|
* Revision $Revision: 6451 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml.classdiagram;
|
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.CommandHideShow3;
|
||||||
import net.sourceforge.plantuml.classdiagram.command.CommandImport;
|
import net.sourceforge.plantuml.classdiagram.command.CommandImport;
|
||||||
import net.sourceforge.plantuml.classdiagram.command.CommandLinkClass2;
|
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.CommandMultilinesClassNote;
|
||||||
import net.sourceforge.plantuml.classdiagram.command.CommandNamespace;
|
import net.sourceforge.plantuml.classdiagram.command.CommandNamespace;
|
||||||
import net.sourceforge.plantuml.classdiagram.command.CommandStereotype;
|
import net.sourceforge.plantuml.classdiagram.command.CommandStereotype;
|
||||||
@ -83,7 +83,7 @@ public class ClassDiagramFactory extends AbstractUmlSystemCommandFactory {
|
|||||||
|
|
||||||
//addCommand(new CommandLinkClass(system));
|
//addCommand(new CommandLinkClass(system));
|
||||||
addCommand(new CommandLinkClass2(system));
|
addCommand(new CommandLinkClass2(system));
|
||||||
addCommand(new CommandLinkLollipop(system));
|
addCommand(new CommandLinkLollipop2(system));
|
||||||
|
|
||||||
addCommand(new CommandImport(system));
|
addCommand(new CommandImport(system));
|
||||||
addCommand(new CommandNoteEntity(system));
|
addCommand(new CommandNoteEntity(system));
|
||||||
|
@ -45,22 +45,32 @@ import net.sourceforge.plantuml.command.regex.RegexOr;
|
|||||||
import net.sourceforge.plantuml.command.regex.RegexPartialMatch;
|
import net.sourceforge.plantuml.command.regex.RegexPartialMatch;
|
||||||
import net.sourceforge.plantuml.cucadiagram.Entity;
|
import net.sourceforge.plantuml.cucadiagram.Entity;
|
||||||
import net.sourceforge.plantuml.cucadiagram.EntityType;
|
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;
|
import net.sourceforge.plantuml.cucadiagram.Stereotype;
|
||||||
|
|
||||||
public class CommandCreateEntityClass2 extends SingleLineCommand2<ClassDiagram> {
|
public class CommandCreateEntityClass2 extends SingleLineCommand2<ClassDiagram> {
|
||||||
|
|
||||||
|
enum Mode {
|
||||||
|
EXTENDS, IMPLEMENTS
|
||||||
|
};
|
||||||
|
|
||||||
public CommandCreateEntityClass2(ClassDiagram diagram) {
|
public CommandCreateEntityClass2(ClassDiagram diagram) {
|
||||||
super(diagram, getRegexConcat());
|
super(diagram, getRegexConcat());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RegexConcat getRegexConcat() {
|
private static RegexConcat getRegexConcat() {
|
||||||
return new RegexConcat(new RegexLeaf("^"),
|
return new RegexConcat(new RegexLeaf("^"), //
|
||||||
new RegexLeaf("TYPE", "(interface|enum|abstract\\s+class|abstract|class)\\s+"),
|
new RegexLeaf("TYPE",//
|
||||||
new RegexOr(
|
"(interface|enum|abstract\\s+class|abstract|class)\\s+"), //
|
||||||
new RegexLeaf("NAME1", "(?:\"([^\"]+)\"\\s+as\\s+)?(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)"),
|
new RegexOr(new RegexLeaf("NAME1",
|
||||||
new RegexLeaf("NAME2", "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s+as\\s+\"([^\"]+)\""),
|
"(?:\"([^\"]+)\"\\s+as\\s+)?(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)"), //
|
||||||
new RegexLeaf("NAME3", "\"([^\"]+)\"")),
|
new RegexLeaf("NAME2", "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s+as\\s+\"([^\"]+)\""), //
|
||||||
new RegexLeaf("STEREO", "(?:\\s*([\\<\\[]{2}.*[\\>\\]]{2}))?"),
|
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("$"));
|
new RegexLeaf("$"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,9 +101,34 @@ public class CommandCreateEntityClass2 extends SingleLineCommand2<ClassDiagram>
|
|||||||
entity.setStereotype(new Stereotype(stereotype, getSystem().getSkinParam().getCircledCharacterRadius(),
|
entity.setStereotype(new Stereotype(stereotype, getSystem().getSkinParam().getCircledCharacterRadius(),
|
||||||
getSystem().getSkinParam().getFont(FontParam.CIRCLED_CHARACTER, null)));
|
getSystem().getSkinParam().getFont(FontParam.CIRCLED_CHARACTER, null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
manageExtends(getSystem(), arg, entity);
|
||||||
|
|
||||||
return CommandExecutionResult.ok();
|
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
|
// @Override
|
||||||
// protected CommandExecutionResult executeArg(List<String> arg) {
|
// protected CommandExecutionResult executeArg(List<String> arg) {
|
||||||
// final String arg0 = arg.get(0).toUpperCase();
|
// final String arg0 = arg.get(0).toUpperCase();
|
||||||
|
@ -55,19 +55,19 @@ public class CommandCreateEntityClassMultilines2 extends CommandMultilines2<Clas
|
|||||||
public CommandCreateEntityClassMultilines2(ClassDiagram diagram) {
|
public CommandCreateEntityClassMultilines2(ClassDiagram diagram) {
|
||||||
super(diagram, getRegexConcat(), "(?i)^\\s*\\}\\s*$");
|
super(diagram, getRegexConcat(), "(?i)^\\s*\\}\\s*$");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RegexConcat getRegexConcat() {
|
private static RegexConcat getRegexConcat() {
|
||||||
return new RegexConcat(new RegexLeaf("^"),
|
return new RegexConcat(new RegexLeaf("^"), //
|
||||||
new RegexLeaf("TYPE", "(interface|enum|abstract\\s+class|abstract|class)\\s+"),
|
new RegexLeaf("TYPE", "(interface|enum|abstract\\s+class|abstract|class)\\s+"), //
|
||||||
new RegexOr(
|
new RegexOr( //
|
||||||
new RegexLeaf("NAME1", "(?:\"([^\"]+)\"\\s+as\\s+)?(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)"),
|
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("NAME2", "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s+as\\s+\"([^\"]+)\""), //
|
||||||
new RegexLeaf("NAME3", "\"([^\"]+)\"")),
|
new RegexLeaf("NAME3", "\"([^\"]+)\"")), //
|
||||||
new RegexLeaf("STEREO", "(?:\\s*([\\<\\[]{2}.*[\\>\\]]{2}))?"),
|
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*$"));
|
new RegexLeaf("\\s*\\{\\s*$"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public CommandExecutionResult execute(List<String> lines) {
|
public CommandExecutionResult execute(List<String> lines) {
|
||||||
StringUtils.trim(lines, true);
|
StringUtils.trim(lines, true);
|
||||||
final Map<String, RegexPartialMatch> line0 = getStartingPattern().matcher(lines.get(0).trim());
|
final Map<String, RegexPartialMatch> line0 = getStartingPattern().matcher(lines.get(0).trim());
|
||||||
@ -82,11 +82,14 @@ public class CommandCreateEntityClassMultilines2 extends CommandMultilines2<Clas
|
|||||||
}
|
}
|
||||||
entity.addFieldOrMethod(s);
|
entity.addFieldOrMethod(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CommandCreateEntityClass2.manageExtends(getSystem(), line0, entity);
|
||||||
|
|
||||||
return CommandExecutionResult.ok();
|
return CommandExecutionResult.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Entity executeArg0(Map<String, RegexPartialMatch> arg) {
|
private Entity executeArg0(Map<String, RegexPartialMatch> arg) {
|
||||||
|
|
||||||
final EntityType type = EntityType.getEntityType(arg.get("TYPE").get(0).toUpperCase());
|
final EntityType type = EntityType.getEntityType(arg.get("TYPE").get(0).toUpperCase());
|
||||||
final String code;
|
final String code;
|
||||||
final String display;
|
final String display;
|
||||||
|
@ -72,8 +72,9 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
|
|||||||
new RegexLeaf("\\s*"),
|
new RegexLeaf("\\s*"),
|
||||||
new RegexLeaf("FIRST_LABEL", "(?:\"([^\"]+)\")?"),
|
new RegexLeaf("FIRST_LABEL", "(?:\"([^\"]+)\")?"),
|
||||||
new RegexLeaf("\\s*"),
|
new RegexLeaf("\\s*"),
|
||||||
new RegexOr(new RegexLeaf("LEFT_TO_RIGHT",
|
new RegexOr(
|
||||||
"(([-=.]+)(?:(left|right|up|down|le?|ri?|up?|do?)(?=[-=.]))?([-=.]*)(o +|[\\]>*+]|\\|[>\\]])?)"),
|
new RegexLeaf("LEFT_TO_RIGHT",
|
||||||
|
"(([-=.]+)(?:(left|right|up|down|le?|ri?|up?|do?)(?=[-=.]))?([-=.]*)(o +|[\\]>*+]|\\|[>\\]])?)"),
|
||||||
new RegexLeaf("RIGHT_TO_LEFT",
|
new RegexLeaf("RIGHT_TO_LEFT",
|
||||||
"(( +o|[\\[<*+]|[<\\[]\\|)?([-=.]*)(left|right|up|down|le?|ri?|up?|do?)?([-=.]+))"),
|
"(( +o|[\\[<*+]|[<\\[]\\|)?([-=.]*)(left|right|up|down|le?|ri?|up?|do?)?([-=.]+))"),
|
||||||
new RegexLeaf("NAV_AGREG_OR_COMPO_INV",
|
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*(\\<\\<.*\\>\\>)?"),
|
+ "(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*|\"[^\"]+\")\\s*(\\<\\<.*\\>\\>)?"),
|
||||||
new RegexLeaf("COUPLE2",
|
new RegexLeaf("COUPLE2",
|
||||||
"\\(\\s*(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s*,\\s*(\\.?[\\p{L}0-9_]+(?:\\.[\\p{L}0-9_]+)*)\\s*\\)")),
|
"\\(\\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) {
|
private static String optionalKeywords(UmlDiagramType type) {
|
||||||
@ -160,8 +165,22 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
|
|||||||
dir = dir.getInv();
|
dir = dir.getInv();
|
||||||
}
|
}
|
||||||
|
|
||||||
Link link = new Link(cl1, cl2, linkType, arg.get("LABEL_LINK").get(0), queue.length(), arg.get("FIRST_LABEL")
|
String firstLabel = arg.get("FIRST_LABEL").get(0);
|
||||||
.get(0), arg.get("SECOND_LABEL").get(0), getSystem().getLabeldistance(), getSystem().getLabelangle());
|
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) {
|
if (dir == Direction.LEFT || dir == Direction.UP) {
|
||||||
link = link.getInv();
|
link = link.getInv();
|
||||||
@ -173,6 +192,20 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
|
|||||||
return CommandExecutionResult.ok();
|
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) {
|
private void addLink(Link link, String weight) {
|
||||||
getSystem().addLink(link);
|
getSystem().addLink(link);
|
||||||
if (weight == null) {
|
if (weight == null) {
|
||||||
@ -196,8 +229,8 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CommandExecutionResult executePackageLink(Map<String, RegexPartialMatch> arg) {
|
private CommandExecutionResult executePackageLink(Map<String, RegexPartialMatch> arg) {
|
||||||
final String ent1 = arg.get("ENT1").get(1);
|
final String ent1 = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.get("ENT1").get(1));
|
||||||
final String ent2 = arg.get("ENT2").get(1);
|
final String ent2 = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.get("ENT2").get(1));
|
||||||
final Group cl1 = getSystem().getGroup(ent1);
|
final Group cl1 = getSystem().getGroup(ent1);
|
||||||
final Group cl2 = getSystem().getGroup(ent2);
|
final Group cl2 = getSystem().getGroup(ent2);
|
||||||
|
|
||||||
@ -210,9 +243,11 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
|
|||||||
queue = getQueue(arg);
|
queue = getQueue(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
Link link = new Link(cl1.getEntityCluster(), cl2.getEntityCluster(), linkType, arg.get("LABEL_LINK").get(0),
|
final String labelLink = arg.get("LABEL_LINK").get(0);
|
||||||
queue.length(), arg.get("FIRST_LABEL").get(0), arg.get("SECOND_LABEL").get(0), getSystem()
|
final String firstLabel = arg.get("FIRST_LABEL").get(0);
|
||||||
.getLabeldistance(), getSystem().getLabelangle());
|
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) {
|
if (dir == Direction.LEFT || dir == Direction.UP) {
|
||||||
link = link.getInv();
|
link = link.getInv();
|
||||||
}
|
}
|
||||||
@ -232,7 +267,7 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
|
|||||||
return CommandExecutionResult.error("No class " + clName2);
|
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 IEntity cl2 = getSystem().getOrCreateClass(ent2);
|
||||||
|
|
||||||
final LinkType linkType = getLinkType(arg);
|
final LinkType linkType = getLinkType(arg);
|
||||||
@ -258,7 +293,7 @@ final public class CommandLinkClass2 extends SingleLineCommand2<AbstractClassOrO
|
|||||||
return CommandExecutionResult.error("No class " + clName2);
|
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 IEntity cl1 = getSystem().getOrCreateClass(ent1);
|
||||||
|
|
||||||
final LinkType linkType = getLinkType(arg);
|
final LinkType linkType = getLinkType(arg);
|
||||||
|
@ -70,7 +70,7 @@ final public class CommandLinkLollipop extends SingleLineCommand<AbstractClassOr
|
|||||||
private final Pattern patternAssociationPoint = Pattern
|
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*\\)");
|
.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(
|
super(
|
||||||
diagram,
|
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*"
|
"(?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*"
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -35,23 +35,32 @@ package net.sourceforge.plantuml.classdiagram.command;
|
|||||||
|
|
||||||
import java.util.List;
|
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.CommandExecutionResult;
|
||||||
import net.sourceforge.plantuml.command.SingleLineCommand;
|
import net.sourceforge.plantuml.command.SingleLineCommand;
|
||||||
import net.sourceforge.plantuml.cucadiagram.Entity;
|
import net.sourceforge.plantuml.cucadiagram.Entity;
|
||||||
|
|
||||||
public class CommandUrl extends SingleLineCommand<ClassDiagram> {
|
public class CommandUrl extends SingleLineCommand<AbstractEntityDiagram> {
|
||||||
|
|
||||||
public CommandUrl(ClassDiagram classDiagram) {
|
public CommandUrl(AbstractEntityDiagram diagram) {
|
||||||
super(classDiagram, "(?i)^url\\s*(?:of|for)?\\s+([\\p{L}0-9_.]+|\"[^\"]+\")\\s+(?:is)?\\s*\\[\\[(.*)\\]\\]$");
|
super(diagram,
|
||||||
|
"(?i)^url\\s*(?:of|for)?\\s+([\\p{L}0-9_.]+|\"[^\"]+\")\\s+(?:is)?\\s*\\[\\[([^|]*)(?:\\|([^|]*))?\\]\\]$");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CommandExecutionResult executeArg(List<String> arg) {
|
protected CommandExecutionResult executeArg(List<String> arg) {
|
||||||
final String code = arg.get(0);
|
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);
|
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();
|
return CommandExecutionResult.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 5980 $
|
* Revision $Revision: 6362 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml.code;
|
package net.sourceforge.plantuml.code;
|
||||||
@ -50,7 +50,16 @@ public class ArobaseStringCompressor implements StringCompressor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String decompress(String s) throws IOException {
|
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) {
|
private String clean(String s) {
|
||||||
@ -70,5 +79,4 @@ public class ArobaseStringCompressor implements StringCompressor {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 5751 $
|
* Revision $Revision: 6486 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml.command;
|
package net.sourceforge.plantuml.command;
|
||||||
@ -37,6 +37,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import net.sourceforge.plantuml.StringUtils;
|
import net.sourceforge.plantuml.StringUtils;
|
||||||
import net.sourceforge.plantuml.UniqueSequence;
|
import net.sourceforge.plantuml.UniqueSequence;
|
||||||
|
import net.sourceforge.plantuml.Url;
|
||||||
import net.sourceforge.plantuml.classdiagram.AbstractEntityDiagram;
|
import net.sourceforge.plantuml.classdiagram.AbstractEntityDiagram;
|
||||||
import net.sourceforge.plantuml.cucadiagram.Entity;
|
import net.sourceforge.plantuml.cucadiagram.Entity;
|
||||||
import net.sourceforge.plantuml.cucadiagram.EntityType;
|
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.Link;
|
||||||
import net.sourceforge.plantuml.cucadiagram.LinkDecor;
|
import net.sourceforge.plantuml.cucadiagram.LinkDecor;
|
||||||
import net.sourceforge.plantuml.cucadiagram.LinkType;
|
import net.sourceforge.plantuml.cucadiagram.LinkType;
|
||||||
|
import net.sourceforge.plantuml.sequencediagram.Note;
|
||||||
|
|
||||||
public abstract class AbstractCommandMultilinesNoteEntity extends CommandMultilines<AbstractEntityDiagram> {
|
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 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 String s = StringUtils.getMergedLines(strings);
|
||||||
|
|
||||||
final Entity note = getSystem().createEntity("GMN" + UniqueSequence.getValue(), s, EntityType.NOTE);
|
final Entity note = getSystem().createEntity("GMN" + UniqueSequence.getValue(), s, EntityType.NOTE);
|
||||||
note.setSpecificBackcolor(line0.get(2));
|
note.setSpecificBackcolor(line0.get(2));
|
||||||
|
note.setUrl(url);
|
||||||
|
|
||||||
final Position position = Position.valueOf(pos.toUpperCase()).withRankdir(getSystem().getRankdir());
|
final Position position = Position.valueOf(pos.toUpperCase()).withRankdir(getSystem().getRankdir());
|
||||||
final Link link;
|
final Link link;
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6214 $
|
* Revision $Revision: 6423 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml.command;
|
package net.sourceforge.plantuml.command;
|
||||||
@ -38,11 +38,19 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.sourceforge.plantuml.DiagramType;
|
||||||
import net.sourceforge.plantuml.UmlDiagram;
|
import net.sourceforge.plantuml.UmlDiagram;
|
||||||
|
|
||||||
public abstract class AbstractUmlSystemCommandFactory implements PSystemCommandFactory {
|
public abstract class AbstractUmlSystemCommandFactory implements PSystemCommandFactory {
|
||||||
|
|
||||||
|
private final DiagramType type;
|
||||||
|
|
||||||
protected AbstractUmlSystemCommandFactory() {
|
protected AbstractUmlSystemCommandFactory() {
|
||||||
|
this(DiagramType.UML);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractUmlSystemCommandFactory(DiagramType type) {
|
||||||
|
this.type = type;
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,4 +128,8 @@ public abstract class AbstractUmlSystemCommandFactory implements PSystemCommandF
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public DiagramType getDiagramType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,11 +35,11 @@ package net.sourceforge.plantuml.command;
|
|||||||
|
|
||||||
import java.util.List;
|
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*)$");
|
super(diagram, "(?i)^\\s*('.*||/'.*'/\\s*)$");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,11 +35,11 @@ package net.sourceforge.plantuml.command;
|
|||||||
|
|
||||||
import java.util.List;
|
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*$");
|
super(diagram, "(?i)^\\s*$");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 5769 $
|
* Revision $Revision: 6448 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml.command;
|
package net.sourceforge.plantuml.command;
|
||||||
@ -40,12 +40,13 @@ import net.sourceforge.plantuml.UmlDiagram;
|
|||||||
public class CommandSkinParam extends SingleLineCommand<UmlDiagram> {
|
public class CommandSkinParam extends SingleLineCommand<UmlDiagram> {
|
||||||
|
|
||||||
public CommandSkinParam(UmlDiagram diagram) {
|
public CommandSkinParam(UmlDiagram diagram) {
|
||||||
super(diagram, "(?i)^skinparam\\s+([\\w.]*(?:\\<\\<.*\\>\\>)?[\\w.]*)\\s+([^{}]*)$");
|
super(diagram, "(?i)^(skinparam|skinparamlocked)\\s+([\\w.]*(?:\\<\\<.*\\>\\>)?[\\w.]*)\\s+([^{}]*)$");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CommandExecutionResult executeArg(List<String> arg) {
|
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();
|
return CommandExecutionResult.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,14 +28,12 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6186 $
|
* Revision $Revision: 6280 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml.command;
|
package net.sourceforge.plantuml.command;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@ -44,8 +42,6 @@ import net.sourceforge.plantuml.StringUtils;
|
|||||||
|
|
||||||
public abstract class SingleLineCommand<S extends PSystem> implements Command {
|
public abstract class SingleLineCommand<S extends PSystem> implements Command {
|
||||||
|
|
||||||
private static final Set<String> printed = new HashSet<String>();
|
|
||||||
|
|
||||||
private final S system;
|
private final S system;
|
||||||
private final Pattern pattern;
|
private final Pattern pattern;
|
||||||
|
|
||||||
@ -60,10 +56,6 @@ public abstract class SingleLineCommand<S extends PSystem> implements Command {
|
|||||||
throw new IllegalArgumentException("Bad pattern " + pattern);
|
throw new IllegalArgumentException("Bad pattern " + pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (printed.add(pattern) == true) {
|
|
||||||
// System.out.println(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.system = system;
|
this.system = system;
|
||||||
this.pattern = Pattern.compile(pattern);
|
this.pattern = Pattern.compile(pattern);
|
||||||
}
|
}
|
||||||
|
@ -28,11 +28,12 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 5463 $
|
* Revision $Revision: 6396 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml.componentdiagram;
|
package net.sourceforge.plantuml.componentdiagram;
|
||||||
|
|
||||||
|
import net.sourceforge.plantuml.classdiagram.command.CommandUrl;
|
||||||
import net.sourceforge.plantuml.command.AbstractUmlSystemCommandFactory;
|
import net.sourceforge.plantuml.command.AbstractUmlSystemCommandFactory;
|
||||||
import net.sourceforge.plantuml.command.CommandCreateNote;
|
import net.sourceforge.plantuml.command.CommandCreateNote;
|
||||||
import net.sourceforge.plantuml.command.CommandEndPackage;
|
import net.sourceforge.plantuml.command.CommandEndPackage;
|
||||||
@ -71,6 +72,7 @@ public class ComponentDiagramFactory extends AbstractUmlSystemCommandFactory {
|
|||||||
addCommand(new CommandNoteEntity(system));
|
addCommand(new CommandNoteEntity(system));
|
||||||
|
|
||||||
addCommand(new CommandCreateNote(system));
|
addCommand(new CommandCreateNote(system));
|
||||||
|
addCommand(new CommandUrl(system));
|
||||||
addCommand(new CommandCreateComponent(system));
|
addCommand(new CommandCreateComponent(system));
|
||||||
addCommand(new CommandCreateCircleInterface(system));
|
addCommand(new CommandCreateCircleInterface(system));
|
||||||
addCommand(new CommandCreateActorInComponent(system));
|
addCommand(new CommandCreateActorInComponent(system));
|
||||||
|
@ -28,15 +28,18 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6229 $
|
* Revision $Revision: 6453 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml.cucadiagram;
|
package net.sourceforge.plantuml.cucadiagram;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -52,13 +55,13 @@ import java.util.TreeMap;
|
|||||||
import net.sourceforge.plantuml.FileFormat;
|
import net.sourceforge.plantuml.FileFormat;
|
||||||
import net.sourceforge.plantuml.FileFormatOption;
|
import net.sourceforge.plantuml.FileFormatOption;
|
||||||
import net.sourceforge.plantuml.Log;
|
import net.sourceforge.plantuml.Log;
|
||||||
import net.sourceforge.plantuml.OptionFlags;
|
|
||||||
import net.sourceforge.plantuml.UmlDiagram;
|
import net.sourceforge.plantuml.UmlDiagram;
|
||||||
import net.sourceforge.plantuml.UmlDiagramType;
|
import net.sourceforge.plantuml.UmlDiagramType;
|
||||||
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramFileMaker;
|
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramFileMaker;
|
||||||
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramFileMakerBeta;
|
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramFileMakerBeta;
|
||||||
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramPngMaker3;
|
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramPngMaker3;
|
||||||
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramTxtMaker;
|
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramTxtMaker;
|
||||||
|
import net.sourceforge.plantuml.png.PngSplitter;
|
||||||
import net.sourceforge.plantuml.skin.VisibilityModifier;
|
import net.sourceforge.plantuml.skin.VisibilityModifier;
|
||||||
import net.sourceforge.plantuml.xmi.CucaDiagramXmiMaker;
|
import net.sourceforge.plantuml.xmi.CucaDiagramXmiMaker;
|
||||||
|
|
||||||
@ -272,33 +275,37 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
|
|||||||
|
|
||||||
abstract protected List<String> getDotStrings();
|
abstract protected List<String> getDotStrings();
|
||||||
|
|
||||||
final public List<File> createFiles(File suggestedFile, FileFormatOption fileFormatOption) throws IOException,
|
// final public List<File> createFiles(File suggestedFile, FileFormatOption
|
||||||
InterruptedException {
|
// 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();
|
private void createFilesXmi(OutputStream suggestedFile, FileFormat fileFormat) throws IOException {
|
||||||
|
|
||||||
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 {
|
|
||||||
final CucaDiagramXmiMaker maker = new CucaDiagramXmiMaker(this, fileFormat);
|
final CucaDiagramXmiMaker maker = new CucaDiagramXmiMaker(this, fileFormat);
|
||||||
return maker.createFiles(suggestedFile);
|
maker.createFiles(suggestedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<File> createFilesTxt(File suggestedFile, FileFormat fileFormat) throws IOException {
|
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;
|
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();
|
final FileFormat fileFormat = fileFormatOption.getFileFormat();
|
||||||
if (fileFormat == FileFormat.ATXT || fileFormat == FileFormat.UTXT) {
|
if (fileFormat == FileFormat.ATXT || fileFormat == FileFormat.UTXT) {
|
||||||
createFilesTxt(os, index, fileFormat);
|
createFilesTxt(os, index, fileFormat);
|
||||||
return;
|
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) {
|
if (getUmlDiagramType() == UmlDiagramType.COMPOSITE) {
|
||||||
final CucaDiagramFileMakerBeta maker = new CucaDiagramFileMakerBeta(this);
|
final CucaDiagramFileMakerBeta maker = new CucaDiagramFileMakerBeta(this);
|
||||||
try {
|
try {
|
||||||
@ -325,9 +383,12 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final CucaDiagramFileMaker maker = new CucaDiagramFileMaker(this);
|
final CucaDiagramFileMaker maker = new CucaDiagramFileMaker(this, flashcodes);
|
||||||
try {
|
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) {
|
} catch (InterruptedException e) {
|
||||||
Log.error(e.toString());
|
Log.error(e.toString());
|
||||||
throw new IOException(e.toString());
|
throw new IOException(e.toString());
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6169 $
|
* Revision $Revision: 6482 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml.cucadiagram;
|
package net.sourceforge.plantuml.cucadiagram;
|
||||||
@ -41,6 +41,7 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import net.sourceforge.plantuml.UniqueSequence;
|
import net.sourceforge.plantuml.UniqueSequence;
|
||||||
|
import net.sourceforge.plantuml.Url;
|
||||||
import net.sourceforge.plantuml.cucadiagram.dot.DrawFile;
|
import net.sourceforge.plantuml.cucadiagram.dot.DrawFile;
|
||||||
import net.sourceforge.plantuml.graphic.HtmlColor;
|
import net.sourceforge.plantuml.graphic.HtmlColor;
|
||||||
import net.sourceforge.plantuml.skin.VisibilityModifier;
|
import net.sourceforge.plantuml.skin.VisibilityModifier;
|
||||||
@ -62,7 +63,7 @@ public class Entity implements IEntity {
|
|||||||
private Group container;
|
private Group container;
|
||||||
|
|
||||||
private DrawFile imageFile;
|
private DrawFile imageFile;
|
||||||
private String url;
|
private Url url2;
|
||||||
|
|
||||||
private boolean top;
|
private boolean top;
|
||||||
|
|
||||||
@ -129,7 +130,7 @@ public class Entity implements IEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<Member> getMethodsToDisplay() {
|
public List<Member> getMethodsToDisplay() {
|
||||||
if (hides==null || hides.size() == 0) {
|
if (hides == null || hides.size() == 0) {
|
||||||
return Collections.unmodifiableList(methods);
|
return Collections.unmodifiableList(methods);
|
||||||
}
|
}
|
||||||
final List<Member> result = new ArrayList<Member>();
|
final List<Member> result = new ArrayList<Member>();
|
||||||
@ -142,7 +143,7 @@ public class Entity implements IEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<Member> getFieldsToDisplay() {
|
public List<Member> getFieldsToDisplay() {
|
||||||
if (hides==null || hides.size() == 0) {
|
if (hides == null || hides.size() == 0) {
|
||||||
return Collections.unmodifiableList(fields);
|
return Collections.unmodifiableList(fields);
|
||||||
}
|
}
|
||||||
final List<Member> result = new ArrayList<Member>();
|
final List<Member> result = new ArrayList<Member>();
|
||||||
@ -237,13 +238,12 @@ public class Entity implements IEntity {
|
|||||||
this.specificBackcolor = HtmlColor.getColorIfValid(s);
|
this.specificBackcolor = HtmlColor.getColorIfValid(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final String getUrl() {
|
public final Url getUrl() {
|
||||||
return url;
|
return url2;
|
||||||
// return "http://www.google.com";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setUrl(String url) {
|
public final void setUrl(Url url) {
|
||||||
this.url = url;
|
this.url2 = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -280,4 +280,15 @@ public class Entity implements IEntity {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean nearDecoration = false;
|
||||||
|
|
||||||
|
public final boolean hasNearDecoration() {
|
||||||
|
return nearDecoration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setNearDecoration(boolean nearDecoration) {
|
||||||
|
this.nearDecoration = nearDecoration;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.sourceforge.plantuml.Url;
|
||||||
import net.sourceforge.plantuml.cucadiagram.dot.DrawFile;
|
import net.sourceforge.plantuml.cucadiagram.dot.DrawFile;
|
||||||
import net.sourceforge.plantuml.graphic.HtmlColor;
|
import net.sourceforge.plantuml.graphic.HtmlColor;
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ public abstract class EntityUtils {
|
|||||||
return ent.getUid();
|
return ent.getUid();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUrl() {
|
public Url getUrl() {
|
||||||
return ent.getUrl();
|
return ent.getUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +127,14 @@ public abstract class EntityUtils {
|
|||||||
ent.setTop(top);
|
ent.setTop(top);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasNearDecoration() {
|
||||||
|
return ent.hasNearDecoration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNearDecoration(boolean nearDecoration) {
|
||||||
|
ent.setNearDecoration(nearDecoration);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ import java.io.IOException;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import net.sourceforge.plantuml.SpecificBackcolorable;
|
import net.sourceforge.plantuml.SpecificBackcolorable;
|
||||||
|
import net.sourceforge.plantuml.Url;
|
||||||
import net.sourceforge.plantuml.cucadiagram.dot.DrawFile;
|
import net.sourceforge.plantuml.cucadiagram.dot.DrawFile;
|
||||||
|
|
||||||
public interface IEntity extends Imaged, SpecificBackcolorable {
|
public interface IEntity extends Imaged, SpecificBackcolorable {
|
||||||
@ -50,7 +51,7 @@ public interface IEntity extends Imaged, SpecificBackcolorable {
|
|||||||
|
|
||||||
public String getUid();
|
public String getUid();
|
||||||
|
|
||||||
public String getUrl();
|
public Url getUrl();
|
||||||
|
|
||||||
public List<Member> getFieldsToDisplay();
|
public List<Member> getFieldsToDisplay();
|
||||||
|
|
||||||
@ -67,6 +68,9 @@ public interface IEntity extends Imaged, SpecificBackcolorable {
|
|||||||
public boolean isTop();
|
public boolean isTop();
|
||||||
|
|
||||||
public void setTop(boolean top);
|
public void setTop(boolean top);
|
||||||
|
|
||||||
|
public boolean hasNearDecoration();
|
||||||
|
|
||||||
|
public void setNearDecoration(boolean nearDecoration);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6152 $
|
* Revision $Revision: 6356 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml.cucadiagram;
|
package net.sourceforge.plantuml.cucadiagram;
|
||||||
@ -68,6 +68,7 @@ public class Link implements Imaged {
|
|||||||
private final String labelangle;
|
private final String labelangle;
|
||||||
|
|
||||||
private HtmlColor specificColor;
|
private HtmlColor specificColor;
|
||||||
|
private boolean constraint = true;
|
||||||
|
|
||||||
public Link(IEntity cl1, IEntity cl2, LinkType type, String label, int length) {
|
public Link(IEntity cl1, IEntity cl2, LinkType type, String label, int length) {
|
||||||
this(cl1, cl2, type, label, length, null, null, null, null, null);
|
this(cl1, cl2, type, label, length, null, null, null, null, null);
|
||||||
@ -96,6 +97,12 @@ public class Link implements Imaged {
|
|||||||
this.labeldistance = labeldistance;
|
this.labeldistance = labeldistance;
|
||||||
this.labelangle = labelangle;
|
this.labelangle = labelangle;
|
||||||
this.specificColor = specificColor;
|
this.specificColor = specificColor;
|
||||||
|
if (qualifier1 != null) {
|
||||||
|
cl1.setNearDecoration(true);
|
||||||
|
}
|
||||||
|
if (qualifier2 != null) {
|
||||||
|
cl2.setNearDecoration(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Link getInv() {
|
public Link getInv() {
|
||||||
@ -314,4 +321,12 @@ public class Link implements Imaged {
|
|||||||
this.specificColor = HtmlColor.getColorIfValid(s);
|
this.specificColor = HtmlColor.getColorIfValid(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final boolean isConstraint() {
|
||||||
|
return constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setConstraint(boolean constraint) {
|
||||||
|
this.constraint = constraint;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
44
src/net/sourceforge/plantuml/cucadiagram/dot/BorderMode.java
Normal file
44
src/net/sourceforge/plantuml/cucadiagram/dot/BorderMode.java
Normal 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;
|
||||||
|
|
||||||
|
}
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6241 $
|
* Revision $Revision: 6474 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml.cucadiagram.dot;
|
package net.sourceforge.plantuml.cucadiagram.dot;
|
||||||
@ -47,6 +47,7 @@ import java.io.FileWriter;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
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.Imaged;
|
||||||
import net.sourceforge.plantuml.cucadiagram.Link;
|
import net.sourceforge.plantuml.cucadiagram.Link;
|
||||||
import net.sourceforge.plantuml.cucadiagram.Stereotype;
|
import net.sourceforge.plantuml.cucadiagram.Stereotype;
|
||||||
|
import net.sourceforge.plantuml.eps.EpsGraphics;
|
||||||
import net.sourceforge.plantuml.eps.EpsStrategy;
|
import net.sourceforge.plantuml.eps.EpsStrategy;
|
||||||
import net.sourceforge.plantuml.eps.EpsTitler;
|
import net.sourceforge.plantuml.eps.EpsTitler;
|
||||||
import net.sourceforge.plantuml.eps.SvgToEpsConverter;
|
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.StringBounder;
|
||||||
import net.sourceforge.plantuml.graphic.StringBounderUtils;
|
import net.sourceforge.plantuml.graphic.StringBounderUtils;
|
||||||
import net.sourceforge.plantuml.graphic.VerticalPosition;
|
import net.sourceforge.plantuml.graphic.VerticalPosition;
|
||||||
|
import net.sourceforge.plantuml.png.PngFlashcoder;
|
||||||
import net.sourceforge.plantuml.png.PngIO;
|
import net.sourceforge.plantuml.png.PngIO;
|
||||||
import net.sourceforge.plantuml.png.PngRotation;
|
import net.sourceforge.plantuml.png.PngRotation;
|
||||||
import net.sourceforge.plantuml.png.PngScaler;
|
import net.sourceforge.plantuml.png.PngScaler;
|
||||||
import net.sourceforge.plantuml.png.PngSizer;
|
import net.sourceforge.plantuml.png.PngSizer;
|
||||||
import net.sourceforge.plantuml.png.PngSplitter;
|
|
||||||
import net.sourceforge.plantuml.png.PngTitler;
|
import net.sourceforge.plantuml.png.PngTitler;
|
||||||
import net.sourceforge.plantuml.skin.CircleInterface;
|
import net.sourceforge.plantuml.skin.CircleInterface;
|
||||||
import net.sourceforge.plantuml.skin.Component;
|
import net.sourceforge.plantuml.skin.Component;
|
||||||
@ -110,6 +112,7 @@ import net.sourceforge.plantuml.ugraphic.svg.UGraphicSvg;
|
|||||||
public final class CucaDiagramFileMaker {
|
public final class CucaDiagramFileMaker {
|
||||||
|
|
||||||
private final CucaDiagram diagram;
|
private final CucaDiagram diagram;
|
||||||
|
private final List<BufferedImage> flashcodes;
|
||||||
private final StaticFilesMap staticFilesMap;
|
private final StaticFilesMap staticFilesMap;
|
||||||
private final Rose rose = new Rose();
|
private final Rose rose = new Rose();
|
||||||
|
|
||||||
@ -120,9 +123,10 @@ public final class CucaDiagramFileMaker {
|
|||||||
stringBounder = StringBounderUtils.asStringBounder(builder.getGraphics2D());
|
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());
|
HtmlColor.setForceMonochrome(diagram.getSkinParam().isMonochrome());
|
||||||
this.diagram = diagram;
|
this.diagram = diagram;
|
||||||
|
this.flashcodes = flashcodes;
|
||||||
if (diagram.getUmlDiagramType() == UmlDiagramType.CLASS || diagram.getUmlDiagramType() == UmlDiagramType.OBJECT) {
|
if (diagram.getUmlDiagramType() == UmlDiagramType.CLASS || diagram.getUmlDiagramType() == UmlDiagramType.OBJECT) {
|
||||||
this.staticFilesMap = new StaticFilesMap(diagram.getSkinParam(), diagram.getDpiFactor(null));
|
this.staticFilesMap = new StaticFilesMap(diagram.getSkinParam(), diagram.getDpiFactor(null));
|
||||||
} else {
|
} 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 {
|
static private void traceDotString(String dotString) throws IOException {
|
||||||
final File f = new File("dottmpfile" + UniqueSequence.getValue() + ".tmp");
|
final File f = new File("dottmpfile" + UniqueSequence.getValue() + ".tmp");
|
||||||
PrintWriter pw = null;
|
PrintWriter pw = null;
|
||||||
@ -216,28 +184,19 @@ public final class CucaDiagramFileMaker {
|
|||||||
private String createSvg(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
|
private String createSvg(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
|
|
||||||
|
final StringBuilder cmap = new StringBuilder();
|
||||||
try {
|
try {
|
||||||
deltaY = 0;
|
deltaY = 0;
|
||||||
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
|
String svg = getSvgData(dotStrings, fileFormatOption, cmap);
|
||||||
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('\\', '/');
|
|
||||||
|
|
||||||
final Dimension2D dim = getDimensionSvg(svg);
|
final Dimension2D dim = getDimensionSvg(svg);
|
||||||
if (dim != null) {
|
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 = addTitleSvg(svg, dim.getWidth(), dim.getHeight());
|
||||||
svg = addHeaderSvg(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.entities().values());
|
||||||
// cleanTemporaryFiles(diagram.getLinks());
|
// cleanTemporaryFiles(diagram.getLinks());
|
||||||
}
|
}
|
||||||
|
if (cmap.length() > 0) {
|
||||||
|
return translateXY(cmap.toString(), 0, (int) Math.round(deltaY));
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String removeSvgXmlHeader(String svg) {
|
static String translateXY(String cmap, int deltaX, int deltaY) {
|
||||||
svg = svg
|
if (deltaY == 0) {
|
||||||
.replaceFirst(
|
return cmap;
|
||||||
"(?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;\">");
|
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;
|
return svg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,6 +430,8 @@ public final class CucaDiagramFileMaker {
|
|||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
|
|
||||||
final StringBuilder cmap = new StringBuilder();
|
final StringBuilder cmap = new StringBuilder();
|
||||||
|
double supX = 0;
|
||||||
|
double supY = 0;
|
||||||
try {
|
try {
|
||||||
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
|
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
|
||||||
final String dotString = dotMaker.createDotString();
|
final String dotString = dotMaker.createDotString();
|
||||||
@ -448,6 +468,12 @@ public final class CucaDiagramFileMaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Color background = diagram.getSkinParam().getBackgroundColor().getColor();
|
final Color background = diagram.getSkinParam().getBackgroundColor().getColor();
|
||||||
|
|
||||||
|
supY = getTitlePngHeight(stringBounder);
|
||||||
|
supY += getHeaderPngHeight(stringBounder);
|
||||||
|
|
||||||
|
supX = getOffsetX(stringBounder, im.getWidth());
|
||||||
|
|
||||||
im = addTitle(im, background);
|
im = addTitle(im, background);
|
||||||
im = addFooter(im, background);
|
im = addFooter(im, background);
|
||||||
im = addHeader(im, background);
|
im = addHeader(im, background);
|
||||||
@ -457,6 +483,9 @@ public final class CucaDiagramFileMaker {
|
|||||||
im = PngRotation.process(im);
|
im = PngRotation.process(im);
|
||||||
}
|
}
|
||||||
im = PngSizer.process(im, diagram.getMinwidth());
|
im = PngSizer.process(im, diagram.getMinwidth());
|
||||||
|
|
||||||
|
im = addFlashcode(im, background);
|
||||||
|
|
||||||
PngIO.write(im, os, diagram.getMetadata(), diagram.getDpi(fileFormatOption));
|
PngIO.write(im, os, diagram.getMetadata(), diagram.getDpi(fileFormatOption));
|
||||||
} finally {
|
} finally {
|
||||||
cleanTemporaryFiles(diagram.entities().values());
|
cleanTemporaryFiles(diagram.entities().values());
|
||||||
@ -464,11 +493,18 @@ public final class CucaDiagramFileMaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cmap.length() > 0) {
|
if (cmap.length() > 0) {
|
||||||
return cmap.toString();
|
return translateXY(cmap.toString(), (int) Math.round(supX), (int) Math.round(supY));
|
||||||
}
|
}
|
||||||
return null;
|
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)
|
private String createDot(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
|
final GraphvizMaker dotMaker = populateImagesAndCreateGraphvizMaker(dotStrings, fileFormatOption);
|
||||||
@ -575,6 +611,24 @@ public final class CucaDiagramFileMaker {
|
|||||||
return pngTitler.processImage(im, background, 3);
|
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 {
|
private String addTitleSvg(String svg, double width, double height) throws IOException {
|
||||||
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
|
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
|
||||||
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
|
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
|
||||||
@ -586,6 +640,16 @@ public final class CucaDiagramFileMaker {
|
|||||||
return svgTitler.addTitleSvg(svg, width, height);
|
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 {
|
private String addTitleEps(String eps) throws IOException {
|
||||||
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
|
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.TITLE, null).getColor();
|
||||||
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
|
final String fontFamily = getSkinParam().getFontFamily(FontParam.TITLE, null);
|
||||||
@ -616,8 +680,6 @@ public final class CucaDiagramFileMaker {
|
|||||||
return epsTitler.addTitleEps(eps);
|
return epsTitler.addTitleEps(eps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private String addHeaderSvg(String svg, double width, double height) throws IOException {
|
private String addHeaderSvg(String svg, double width, double height) throws IOException {
|
||||||
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.HEADER, null).getColor();
|
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.HEADER, null).getColor();
|
||||||
final String fontFamily = getSkinParam().getFontFamily(FontParam.HEADER, null);
|
final String fontFamily = getSkinParam().getFontFamily(FontParam.HEADER, null);
|
||||||
@ -628,6 +690,15 @@ public final class CucaDiagramFileMaker {
|
|||||||
return svgTitler.addTitleSvg(svg, width, height);
|
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 {
|
private String addFooterSvg(String svg, double width, double height) throws IOException {
|
||||||
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.FOOTER, null).getColor();
|
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.FOOTER, null).getColor();
|
||||||
final String fontFamily = getSkinParam().getFontFamily(FontParam.FOOTER, null);
|
final String fontFamily = getSkinParam().getFontFamily(FontParam.FOOTER, null);
|
||||||
@ -637,6 +708,15 @@ public final class CucaDiagramFileMaker {
|
|||||||
return svgTitler.addTitleSvg(svg, width, height + deltaY);
|
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) {
|
private BufferedImage addFooter(BufferedImage im, final Color background) {
|
||||||
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.FOOTER, null).getColor();
|
final Color titleColor = diagram.getSkinParam().getFontHtmlColor(FontParam.FOOTER, null).getColor();
|
||||||
final String fontFamily = getSkinParam().getFontFamily(FontParam.FOOTER, null);
|
final String fontFamily = getSkinParam().getFontFamily(FontParam.FOOTER, null);
|
||||||
@ -655,6 +735,15 @@ public final class CucaDiagramFileMaker {
|
|||||||
return pngTitler.processImage(im, background, 3);
|
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 {
|
private void cleanTemporaryFiles(final Collection<? extends Imaged> imageFiles) throws IOException {
|
||||||
if (OptionFlags.getInstance().isKeepTmpFiles() == false) {
|
if (OptionFlags.getInstance().isKeepTmpFiles() == false) {
|
||||||
for (Imaged entity : imageFiles) {
|
for (Imaged entity : imageFiles) {
|
||||||
@ -842,8 +931,6 @@ public final class CucaDiagramFileMaker {
|
|||||||
final Lazy<File> lpng = new Lazy<File>() {
|
final Lazy<File> lpng = new Lazy<File>() {
|
||||||
public File getNow() throws IOException {
|
public File getNow() throws IOException {
|
||||||
final EmptyImageBuilder builder = new EmptyImageBuilder(stickMan.getPreferredWidth(null) * dpiFactor,
|
final EmptyImageBuilder builder = new EmptyImageBuilder(stickMan.getPreferredWidth(null) * dpiFactor,
|
||||||
// stickMan.getPreferredHeight(null) * dpiFactor, dpiFactor > 1
|
|
||||||
// ? Color.BLUE : background);
|
|
||||||
stickMan.getPreferredHeight(null) * dpiFactor, background);
|
stickMan.getPreferredHeight(null) * dpiFactor, background);
|
||||||
|
|
||||||
final BufferedImage im = builder.getBufferedImage();
|
final BufferedImage im = builder.getBufferedImage();
|
||||||
@ -920,10 +1007,11 @@ public final class CucaDiagramFileMaker {
|
|||||||
|
|
||||||
final boolean isUnderline = dotMaker.isUnderline();
|
final boolean isUnderline = dotMaker.isUnderline();
|
||||||
String eps = new String(baos.toByteArray(), "UTF-8");
|
String eps = new String(baos.toByteArray(), "UTF-8");
|
||||||
|
eps = cleanStrangeCharacter(eps);
|
||||||
if (isUnderline) {
|
if (isUnderline) {
|
||||||
eps = new UnderlineTrickEps(eps).getString();
|
eps = new UnderlineTrickEps(eps).getString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (diagram.getTitle() != null) {
|
if (diagram.getTitle() != null) {
|
||||||
eps = addTitleEps(eps);
|
eps = addTitleEps(eps);
|
||||||
}
|
}
|
||||||
@ -975,7 +1063,6 @@ public final class CucaDiagramFileMaker {
|
|||||||
// mImage.appendTail(sb);
|
// mImage.appendTail(sb);
|
||||||
// svg = sb.toString();
|
// svg = sb.toString();
|
||||||
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
// cleanTemporaryFiles(diagram.entities().values());
|
// cleanTemporaryFiles(diagram.entities().values());
|
||||||
// cleanTemporaryFiles(diagram.getLinks());
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -48,10 +48,10 @@ import java.util.Map;
|
|||||||
|
|
||||||
import net.sourceforge.plantuml.FileFormat;
|
import net.sourceforge.plantuml.FileFormat;
|
||||||
import net.sourceforge.plantuml.StringUtils;
|
import net.sourceforge.plantuml.StringUtils;
|
||||||
import net.sourceforge.plantuml.cucadiagram.Member;
|
|
||||||
import net.sourceforge.plantuml.cucadiagram.CucaDiagram;
|
import net.sourceforge.plantuml.cucadiagram.CucaDiagram;
|
||||||
import net.sourceforge.plantuml.cucadiagram.Entity;
|
import net.sourceforge.plantuml.cucadiagram.Entity;
|
||||||
import net.sourceforge.plantuml.cucadiagram.Link;
|
import net.sourceforge.plantuml.cucadiagram.Link;
|
||||||
|
import net.sourceforge.plantuml.cucadiagram.Member;
|
||||||
import net.sourceforge.plantuml.posimo.Block;
|
import net.sourceforge.plantuml.posimo.Block;
|
||||||
import net.sourceforge.plantuml.posimo.Cluster;
|
import net.sourceforge.plantuml.posimo.Cluster;
|
||||||
import net.sourceforge.plantuml.posimo.GraphvizSolverB;
|
import net.sourceforge.plantuml.posimo.GraphvizSolverB;
|
||||||
|
301
src/net/sourceforge/plantuml/cucadiagram/dot/DotCommon.java
Normal file
301
src/net/sourceforge/plantuml/cucadiagram/dot/DotCommon.java
Normal 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
1819
src/net/sourceforge/plantuml/cucadiagram/dot/DotMaker2.java
Normal file
1819
src/net/sourceforge/plantuml/cucadiagram/dot/DotMaker2.java
Normal file
File diff suppressed because it is too large
Load Diff
1813
src/net/sourceforge/plantuml/cucadiagram/dot/DotMakerOk.java
Normal file
1813
src/net/sourceforge/plantuml/cucadiagram/dot/DotMakerOk.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,7 +28,7 @@
|
|||||||
*
|
*
|
||||||
* Original Author: Arnaud Roques
|
* Original Author: Arnaud Roques
|
||||||
*
|
*
|
||||||
* Revision $Revision: 6228 $
|
* Revision $Revision: 6453 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package net.sourceforge.plantuml.cucadiagram.dot;
|
package net.sourceforge.plantuml.cucadiagram.dot;
|
||||||
@ -47,8 +47,6 @@ import java.util.Map;
|
|||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import com.sun.org.apache.bcel.internal.generic.GETSTATIC;
|
|
||||||
|
|
||||||
import net.sourceforge.plantuml.FileFormat;
|
import net.sourceforge.plantuml.FileFormat;
|
||||||
import net.sourceforge.plantuml.FileUtils;
|
import net.sourceforge.plantuml.FileUtils;
|
||||||
import net.sourceforge.plantuml.ISkinParam;
|
import net.sourceforge.plantuml.ISkinParam;
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -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(">");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
Loading…
Reference in New Issue
Block a user