diff --git a/gradle/test/junit5.gradle b/gradle/test/junit5.gradle index 0454148..8ee928f 100644 --- a/gradle/test/junit5.gradle +++ b/gradle/test/junit5.gradle @@ -12,7 +12,11 @@ dependencies { test { useJUnitPlatform() failFast = false + environment 'TMPDIR', '/var/tmp/gs' systemProperty 'java.awt.headless', 'true' + systemProperty 'jna.debug', 'true' + //systemProperty 'jna.library.path', file('src/test/resources/').toString() + systemProperty 'java.util.logging.config.file', 'src/test/resources/logging.properties' testLogging { events 'STARTED', 'PASSED', 'FAILED', 'SKIPPED' showStandardStreams = true diff --git a/graphics-barcode/src/main/java/module-info.java b/graphics-barcode/src/main/java/module-info.java index 1c65a9d..cf80b1a 100644 --- a/graphics-barcode/src/main/java/module-info.java +++ b/graphics-barcode/src/main/java/module-info.java @@ -1,6 +1,10 @@ +import org.xbib.graphics.barcode.Code3Of9; +import org.xbib.graphics.barcode.SymbolProvider; + module org.xbib.graphics.barcode { exports org.xbib.graphics.barcode; exports org.xbib.graphics.barcode.util; exports org.xbib.graphics.barcode.render; requires transitive java.desktop; + provides SymbolProvider with Code3Of9.Provider; } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AbstractSymbol.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AbstractSymbol.java new file mode 100755 index 0000000..b28dffa --- /dev/null +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AbstractSymbol.java @@ -0,0 +1,1036 @@ +package org.xbib.graphics.barcode; + +import static org.xbib.graphics.barcode.HumanReadableLocation.BOTTOM; +import static org.xbib.graphics.barcode.HumanReadableLocation.NONE; +import static org.xbib.graphics.barcode.HumanReadableLocation.TOP; +import org.xbib.graphics.barcode.util.Hexagon; +import org.xbib.graphics.barcode.util.TextBox; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +/** + * Abstract barcode symbology class. + */ +public abstract class AbstractSymbol implements Symbol { + + private final List rectangles = new ArrayList<>(); + + private final List texts = new ArrayList<>(); + + private final List hexagons = new ArrayList<>(); + + private final List target = new ArrayList<>(); + + protected String content; + + protected StringBuilder readable; + + protected String[] pattern; + + protected int rowCount = 0; + + protected int[] rowHeight; + + protected StringBuilder errorMsg = new StringBuilder(); + + protected int symbolHeight = 0; + + protected int symbolWidth = 0; + + protected int defaultHeight = 40; + + private HumanReadableLocation humanReadableLocation = BOTTOM; + + protected StringBuilder encodeInfo = new StringBuilder(); + + protected byte[] inputBytes; + + protected SymbolDataType inputSymbolDataType = SymbolDataType.ECI; + + int moduleWidth = 1; + + double fontSize = 8; + + boolean readerInit; + + int eciMode = 3; + + private int quietZoneHorizontal = 0; + + private int quietZoneVertical = 0; + + private String fontName = "Helvetica"; + + public AbstractSymbol() { + unsetReaderInit(); + } + + public List getRectangles() { + return rectangles; + } + + public List getTexts() { + return texts; + } + + public List getHexagons() { + return hexagons; + } + + public List getTarget() { + return target; + } + + /** + * Inserts the specified array into the specified original array at the specified index. + * + * @param original the original array into which we want to insert another array + * @param index the index at which we want to insert the array + * @param inserted the array that we want to insert + * @return the combined array + */ + protected static int[] insert(int[] original, int index, int[] inserted) { + int[] modified = new int[original.length + inserted.length]; + System.arraycopy(original, 0, modified, 0, index); + System.arraycopy(inserted, 0, modified, index, inserted.length); + System.arraycopy(original, index, modified, index + inserted.length, modified.length - index - inserted.length); + return modified; + } + + /** + * Returns true if the specified array contains the specified value. + * + * @param values the array to check in + * @param value the value to check for + * @return true if the specified array contains the specified value + */ + protected static boolean contains(int[] values, int value) { + for (int value1 : values) { + if (value1 == value) { + return true; + } + } + return false; + } + + /** + * Sets the type of input data. This setting influences what + * pre-processing is done on data before encoding in the symbol. + * For example: for GS1 mode the AI data will be used to + * calculate the position of 'FNC1' characters. + * Valid values are: + *
    + *
  • UTF8 (default) Unicode encoding + *
  • LATIN1 ISO 8859-1 (Latin-1) encoding + *
  • BINARY Byte encoding mode + *
  • GS1 Application Identifier and data pairs in "[AI]DATA" format + *
  • HIBC Health Industry Bar Code number (without check digit) + *
  • ECI Extended Channel Interpretations + *
+ * + * @param symbolDataType A DataType value which specifies the type of data + */ + @Override + public void setDataType(SymbolDataType symbolDataType) { + inputSymbolDataType = symbolDataType; + } + + /** + * Prefixes symbol data with a "Reader Initialisation" or "Reader + * Programming" instruction. + */ + public final void setReaderInit() { + readerInit = true; + } + + /** + * Removes "Reader Initialisation" or "Reader Programming" instruction + * from symbol data. + */ + private void unsetReaderInit() { + readerInit = false; + } + + /** + * Returns the default bar height for this symbol. + * + * @return the default bar height for this symbol + */ + @Override + public int getBarHeight() { + return defaultHeight; + } + + /** + * Sets the default bar height for this symbol (default value is 40). + * + * @param barHeight the default bar height for this symbol + */ + @Override + public void setBarHeight(int barHeight) { + this.defaultHeight = barHeight; + } + + /** + * Returns the module width for this symbol. + * + * @return the module width for this symbol + */ + @Override + public int getModuleWidth() { + return moduleWidth; + } + + /** + * Sets the module width for this symbol (default value is 1). + * + * @param moduleWidth the module width for this symbol + */ + @Override + public void setModuleWidth(int moduleWidth) { + this.moduleWidth = moduleWidth; + } + + /** + * Returns the horizontal quiet zone (white space) added to the left and to the right of this symbol. + * + * @return the horizontal quiet zone (white space) added to the left and to the right of this symbol + */ + @Override + public int getQuietZoneHorizontal() { + return quietZoneHorizontal; + } + + /** + * Sets the horizontal quiet zone (white space) added to the left and to the right of this symbol. + * + * @param quietZoneHorizontal the horizontal quiet zone (white space) added to the left and to the right of this symbol + */ + @Override + public void setQuietZoneHorizontal(int quietZoneHorizontal) { + this.quietZoneHorizontal = quietZoneHorizontal; + } + + /** + * Returns the vertical quiet zone (white space) added above and below this symbol. + * + * @return the vertical quiet zone (white space) added above and below this symbol + */ + @Override + public int getQuietZoneVertical() { + return quietZoneVertical; + } + + /** + * Sets the vertical quiet zone (white space) added above and below this symbol. + * + * @param quietZoneVertical the vertical quiet zone (white space) added above and below this symbol + */ + @Override + public void setQuietZoneVertical(int quietZoneVertical) { + this.quietZoneVertical = quietZoneVertical; + } + + /** + * Returns the name of the font to use to render the human-readable text. + * + * @return the name of the font to use to render the human-readable text + */ + @Override + public String getFontName() { + return fontName; + } + + /** + * Sets the name of the font to use to render the human-readable text (default value is Helvetica). + * + * @param fontName the name of the font to use to render the human-readable text + */ + @Override + public void setFontName(String fontName) { + this.fontName = fontName; + } + + /** + * Returns the size of the font to use to render the human-readable text. + * + * @return the size of the font to use to render the human-readable text + */ + @Override + public double getFontSize() { + return fontSize; + } + + /** + * Sets the size of the font to use to render the human-readable text (default value is 8). + * + * @param fontSize the size of the font to use to render the human-readable text + */ + @Override + public void setFontSize(double fontSize) { + this.fontSize = fontSize; + } + + /** + * Gets the width of the encoded symbol, including the horizontal quiet zone. + * + * @return the width of the encoded symbol + */ + @Override + public int getWidth() { + return symbolWidth + (2 * quietZoneHorizontal); + } + + /** + * Returns the height of the symbol, including the human-readable text, if any, as well as the vertical + * quiet zone. This height is an approximation, since it is calculated without access to a font engine. + * + * @return the height of the symbol, including the human-readable text, if any, as well as the vertical + * quiet zone + */ + @Override + public int getHeight() { + return symbolHeight + getHumanReadableHeight() + (2 * quietZoneVertical); + } + + /** + * Returns the height of the human-readable text, including the space between the text and other symbols. + * This height is an approximation, since it is calculated without access to a font engine. + * + * @return the height of the human-readable text + */ + @Override + public int getHumanReadableHeight() { + if (texts.isEmpty()) { + return 0; + } else { + return getTheoreticalHumanReadableHeight(); + } + } + + /** + * Returns the height of the human-readable text, assuming this symbol had human-readable text. + * + * @return the height of the human-readable text, assuming this symbol had human-readable text + */ + protected int getTheoreticalHumanReadableHeight() { + return (int) Math.ceil(fontSize * 1.2); // 0.2 space between bars and text + } + + /** + * Returns a human readable summary of the decisions made by the encoder when creating a symbol. + * + * @return a human readable summary of the decisions made by the encoder when creating a symbol + */ + public String getEncodeInfo() { + return encodeInfo.toString(); + } + + /** + * Returns the location of the human-readable text. + * + * @return the location of the human-readable text + */ + @Override + public HumanReadableLocation getHumanReadableLocation() { + return humanReadableLocation; + } + + /** + * Sets the location of the human-readable text (default value is {@link HumanReadableLocation#BOTTOM}). + * + * @param humanReadableLocation the location of the human-readable text + */ + @Override + public void setHumanReadableLocation(HumanReadableLocation humanReadableLocation) { + this.humanReadableLocation = humanReadableLocation; + } + + @Override + public String getContent() { + return content; + } + + /** + * Set the data to be encoded. Input data will be assumed to be of + * the type set by setDataType. + * + * @param inputData A String containing the data to encode + */ + @Override + public void setContent(String inputData) { + int i; + content = inputData; + if (inputSymbolDataType == SymbolDataType.GS1) { + content = gs1SanityCheck(inputData); + } + if (inputSymbolDataType == SymbolDataType.GS1) { + readable = new StringBuilder(); + for (i = 0; i < inputData.length(); i++) { + switch (inputData.charAt(i)) { + case '[': + readable.append('('); + break; + case ']': + readable.append(')'); + break; + default: + readable.append(inputData.charAt(i)); + break; + } + } + } + if (inputSymbolDataType == SymbolDataType.HIBC) { + content = hibcProcess(inputData); + } + if (!content.isEmpty()) { + if (!encode()) { + throw new IllegalStateException(errorMsg.toString()); + } + } else { + throw new IllegalStateException("No input data"); + } + } + + + @Override + public void plotSymbol() { + int xBlock, yBlock; + double x, y, w, h; + boolean black; + rectangles.clear(); + texts.clear(); + int baseY; + if (humanReadableLocation == TOP) { + baseY = getTheoreticalHumanReadableHeight(); + } else { + baseY = 0; + } + h = 0; + y = baseY; + for (yBlock = 0; yBlock < rowCount; yBlock++) { + black = true; + x = 0; + for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) { + char c = pattern[yBlock].charAt(xBlock); + w = getModuleWidth(c - '0') * moduleWidth; + if (black) { + if (rowHeight[yBlock] == -1) { + h = defaultHeight; + } else { + h = rowHeight[yBlock]; + } + if (w != 0 && h != 0) { + Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); + rectangles.add(rect); + } + if (x + w > symbolWidth) { + symbolWidth = (int) Math.ceil(x + w); + } + } + black = !black; + x += w; + } + if ((y - baseY + h) > symbolHeight) { + symbolHeight = (int) Math.ceil(y - baseY + h); + } + y += h; + } + mergeVerticalBlocks(); + if (humanReadableLocation != NONE && readable.length() > 0) { + double baseline; + if (humanReadableLocation == TOP) { + baseline = fontSize; + } else { + baseline = getHeight() + fontSize; + } + double centerX = getWidth() / 2.0; + texts.add(new TextBox(centerX, baseline, readable.toString())); + } + } + + protected int positionOf(char thischar, char[] LookUp) { + int i, outval = 0; + + for (i = 0; i < LookUp.length; i++) { + if (thischar == LookUp[i]) { + outval = i; + } + } + return outval; + } + + protected String bin2pat(String bin) { + boolean black; + int i, l; + StringBuilder pat = new StringBuilder(); + + black = true; + l = 0; + for (i = 0; i < bin.length(); i++) { + if (black) { + if (bin.charAt(i) == '1') { + l++; + } else { + black = false; + pat.append((char) (l + '0')); + l = 1; + } + } else { + if (bin.charAt(i) == '0') { + l++; + } else { + black = true; + pat.append((char) (l + '0')); + l = 1; + } + } + } + pat.append((char) (l + '0')); + return pat.toString(); + } + + protected void eciProcess() { + int qmarksBefore, qmarksAfter; + int i; + + qmarksBefore = 0; + for (i = 0; i < content.length(); i++) { + if (content.charAt(i) == '?') { + qmarksBefore++; + } + } + + qmarksAfter = eciEncode("ISO8859_1"); + if (qmarksAfter == qmarksBefore) { + eciMode = 3; + encodeInfo.append("Encoding in ISO 8859-1 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_2"); + if (qmarksAfter == qmarksBefore) { + eciMode = 4; + encodeInfo.append("Encoding in ISO 8859-2 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_3"); + if (qmarksAfter == qmarksBefore) { + eciMode = 5; + encodeInfo.append("Encoding in ISO 8859-3 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_4"); + if (qmarksAfter == qmarksBefore) { + eciMode = 6; + encodeInfo.append("Encoding in ISO 8859-4 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_5"); + if (qmarksAfter == qmarksBefore) { + eciMode = 7; + encodeInfo.append("Encoding in ISO 8859-5 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_6"); + if (qmarksAfter == qmarksBefore) { + eciMode = 8; + encodeInfo.append("Encoding in ISO 8859-6 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_7"); + if (qmarksAfter == qmarksBefore) { + eciMode = 9; + encodeInfo.append("Encoding in ISO 8859-7 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_8"); + if (qmarksAfter == qmarksBefore) { + eciMode = 10; + encodeInfo.append("Encoding in ISO 8859-8 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_9"); + if (qmarksAfter == qmarksBefore) { + eciMode = 11; + encodeInfo.append("Encoding in ISO 8859-9 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_10"); + if (qmarksAfter == qmarksBefore) { + eciMode = 12; + encodeInfo.append("Encoding in ISO 8859-10 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_11"); + if (qmarksAfter == qmarksBefore) { + eciMode = 13; + encodeInfo.append("Encoding in ISO 8859-11 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_13"); + if (qmarksAfter == qmarksBefore) { + eciMode = 15; + encodeInfo.append("Encoding in ISO 8859-13 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_14"); + if (qmarksAfter == qmarksBefore) { + eciMode = 16; + encodeInfo.append("Encoding in ISO 8859-14 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_15"); + if (qmarksAfter == qmarksBefore) { + eciMode = 17; + encodeInfo.append("Encoding in ISO 8859-15 character set\n"); + return; + } + + qmarksAfter = eciEncode("ISO8859_16"); + if (qmarksAfter == qmarksBefore) { + eciMode = 18; + encodeInfo.append("Encoding in ISO 8859-16 character set\n"); + return; + } + + qmarksAfter = eciEncode("Windows_1250"); + if (qmarksAfter == qmarksBefore) { + eciMode = 21; + encodeInfo.append("Encoding in Windows-1250 character set\n"); + return; + } + + qmarksAfter = eciEncode("Windows_1251"); + if (qmarksAfter == qmarksBefore) { + eciMode = 22; + encodeInfo.append("Encoding in Windows-1251 character set\n"); + return; + } + + qmarksAfter = eciEncode("Windows_1252"); + if (qmarksAfter == qmarksBefore) { + eciMode = 23; + encodeInfo.append("Encoding in Windows-1252 character set\n"); + return; + } + + qmarksAfter = eciEncode("Windows_1256"); + if (qmarksAfter == qmarksBefore) { + eciMode = 24; + encodeInfo.append("Encoding in Windows-1256 character set\n"); + return; + } + + qmarksAfter = eciEncode("SJIS"); + if (qmarksAfter == qmarksBefore) { + eciMode = 20; + encodeInfo.append("Encoding in Shift-JIS character set\n"); + return; + } + + /* default */ + qmarksAfter = eciEncode("UTF8"); + eciMode = 26; + encodeInfo.append("Encoding in UTF-8 character set\n"); + } + + /** + * Returns the module width to use for the specified original module width, taking into account any module width ratio + * customizations. Intended to be overridden by subclasses that support such module width ratio customization. + * + * @param originalWidth the original module width + * @return the module width to use for the specified original module width + */ + protected double getModuleWidth(int originalWidth) { + return originalWidth; + } + + /** + * Returns the intermediate coding of this bar code. Symbol types that use the test + * infrastructure should override this method. + * + * @return the intermediate coding of this bar code + */ + protected int[] getCodewords() { + throw new UnsupportedOperationException(); + } + + /** + * Search for rectangles which have the same width and x position, and + * which join together vertically and merge them together to reduce the + * number of rectangles needed to describe a symbol. + */ + protected void mergeVerticalBlocks() { + for (int i = 0; i < rectangles.size() - 1; i++) { + for (int j = i + 1; j < rectangles.size(); j++) { + Rectangle2D.Double firstRect = rectangles.get(i); + Rectangle2D.Double secondRect = rectangles.get(j); + if (roughlyEqual(firstRect.x, secondRect.x) && roughlyEqual(firstRect.width, secondRect.width)) { + if (roughlyEqual(firstRect.y + firstRect.height, secondRect.y)) { + firstRect.height += secondRect.height; + rectangles.set(i, firstRect); + rectangles.remove(j); + } + } + } + } + } + + /** + * Returns this bar code's pattern, converted into a set of corresponding codewords. + * Useful for bar codes that encode their content as a pattern. + * + * @param size the number of digits in each codeword + * @return this bar code's pattern, converted into a set of corresponding codewords + */ + protected int[] getPatternAsCodewords(int size) { + if (pattern == null || pattern.length == 0) { + return new int[0]; + } else { + int count = (int) Math.ceil(pattern[0].length() / (double) size); + int[] codewords = new int[pattern.length * count]; + for (int i = 0; i < pattern.length; i++) { + String row = pattern[i]; + for (int j = 0; j < count; j++) { + int substringStart = j * size; + int substringEnd = Math.min((j + 1) * size, row.length()); + codewords[(i * count) + j] = Integer.parseInt(row.substring(substringStart, substringEnd)); + } + } + return codewords; + } + } + + private int eciEncode(String charset) { + /* getBytes replaces unconverted characters to '?', so count + the number of question marks to find if conversion was sucessful. + */ + int i, qmarksAfter; + + try { + inputBytes = content.getBytes(charset); + } catch (UnsupportedEncodingException e) { + return -1; + } + + qmarksAfter = 0; + for (i = 0; i < inputBytes.length; i++) { + if (inputBytes[i] == '?') { + qmarksAfter++; + } + } + + return qmarksAfter; + } + + private String gs1SanityCheck(String source) { + StringBuilder reduced = new StringBuilder(); + int i, j, lastAi; + boolean aiLatch; + int bracketLevel, maxBracketLevel, aiLength, maxAiLength, minAiLength; + int[] aiValue = new int[100]; + int[] aiLocation = new int[100]; + int aiCount; + int[] dataLocation = new int[100]; + int[] dataLength = new int[100]; + int srcLen = source.length(); + int errorLatch; + for (i = 0; i < srcLen; i++) { + if (source.charAt(i) >= 128) { + errorMsg.append("Extended ASCII characters are not supported by GS1"); + return ""; + } + if (source.charAt(i) < 32) { + errorMsg.append("Control characters are not supported by GS1"); + return ""; + } + } + if (source.charAt(0) != '[') { + errorMsg.append("Data does not start with an AI"); + return ""; + } + bracketLevel = 0; + maxBracketLevel = 0; + aiLength = 0; + maxAiLength = 0; + minAiLength = 5; + j = 0; + aiLatch = false; + for (i = 0; i < srcLen; i++) { + aiLength += j; + if (((j == 1) && (source.charAt(i) != ']')) + && ((source.charAt(i) < '0') || (source.charAt(i) > '9'))) { + aiLatch = true; + } + if (source.charAt(i) == '[') { + bracketLevel++; + j = 1; + } + if (source.charAt(i) == ']') { + bracketLevel--; + if (aiLength < minAiLength) { + minAiLength = aiLength; + } + j = 0; + aiLength = 0; + } + if (bracketLevel > maxBracketLevel) { + maxBracketLevel = bracketLevel; + } + if (aiLength > maxAiLength) { + maxAiLength = aiLength; + } + } + minAiLength--; + if (bracketLevel != 0) { + errorMsg.append("Malformed AI in input data (brackets don't match)"); + return ""; + } + if (maxBracketLevel > 1) { + errorMsg.append("Found nested brackets in input data"); + return ""; + } + if (maxAiLength > 4) { + errorMsg.append("Invalid AI in input data (AI too long)"); + return ""; + } + if (minAiLength <= 1) { + errorMsg.append("Invalid AI in input data (AI too short)"); + return ""; + } + if (aiLatch) { + errorMsg.append("Invalid AI in input data (non-numeric characters in AI)"); + return ""; + } + aiCount = 0; + for (i = 1; i < srcLen; i++) { + if (source.charAt(i - 1) == '[') { + aiLocation[aiCount] = i; + aiValue[aiCount] = 0; + for (j = 0; source.charAt(i + j) != ']'; j++) { + aiValue[aiCount] *= 10; + aiValue[aiCount] += Character.getNumericValue(source.charAt(i + j)); + } + aiCount++; + } + } + for (i = 0; i < aiCount; i++) { + dataLocation[i] = aiLocation[i] + 3; + if (aiValue[i] >= 100) { + dataLocation[i]++; + } + if (aiValue[i] >= 1000) { + dataLocation[i]++; + } + dataLength[i] = source.length() - dataLocation[i]; + for (j = source.length() - 1; j >= dataLocation[i]; j--) { + if (source.charAt(j) == '[') { + dataLength[i] = j - dataLocation[i]; + } + } + } + for (i = 0; i < aiCount; i++) { + if (dataLength[i] == 0) { + errorMsg.append("Empty data field in input data"); + return ""; + } + } + errorLatch = 0; + for (i = 0; i < aiCount; i++) { + switch (aiValue[i]) { + case 0: + if (dataLength[i] != 18) { + errorLatch = 1; + } + break; + case 1: + case 2: + case 3: + if (dataLength[i] != 14) { + errorLatch = 1; + } + break; + case 4: + if (dataLength[i] != 16) { + errorLatch = 1; + } + break; + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + if (dataLength[i] != 6) { + errorLatch = 1; + } + break; + case 20: + if (dataLength[i] != 2) { + errorLatch = 1; + } + break; + case 23: + case 24: + case 25: + case 39: + case 40: + case 41: + case 42: + case 70: + case 80: + case 81: + errorLatch = 2; + break; + } + if ( + ((aiValue[i] >= 100) && (aiValue[i] <= 179)) + || ((aiValue[i] >= 1000) && (aiValue[i] <= 1799)) + || ((aiValue[i] >= 200) && (aiValue[i] <= 229)) + || ((aiValue[i] >= 2000) && (aiValue[i] <= 2299)) + || ((aiValue[i] >= 300) && (aiValue[i] <= 309)) + || ((aiValue[i] >= 3000) && (aiValue[i] <= 3099)) + || ((aiValue[i] >= 31) && (aiValue[i] <= 36)) + || ((aiValue[i] >= 310) && (aiValue[i] <= 369))) { + errorLatch = 2; + } + if ((aiValue[i] >= 3100) && (aiValue[i] <= 3699) && dataLength[i] != 6) { + errorLatch = 1; + } + if ( + ((aiValue[i] >= 370) && (aiValue[i] <= 379)) + || ((aiValue[i] >= 3700) && (aiValue[i] <= 3799))) { + errorLatch = 2; + } + if ((aiValue[i] >= 410) && (aiValue[i] <= 415)) { + if (dataLength[i] != 13) { + errorLatch = 1; + } + } + if ( + ((aiValue[i] >= 4100) && (aiValue[i] <= 4199)) + || ((aiValue[i] >= 700) && (aiValue[i] <= 703)) + || ((aiValue[i] >= 800) && (aiValue[i] <= 810)) + || ((aiValue[i] >= 900) && (aiValue[i] <= 999)) + || ((aiValue[i] >= 9000) && (aiValue[i] <= 9999))) { + errorLatch = 2; + } + + if (errorLatch == 1) { + errorMsg = new StringBuilder("Invalid data length for AI"); + return ""; + } + + if (errorLatch == 2) { + errorMsg = new StringBuilder("Invalid AI value"); + return ""; + } + } + aiLatch = false; + for (i = 0; i < srcLen; i++) { + if ((source.charAt(i) != '[') && (source.charAt(i) != ']')) { + reduced.append(source.charAt(i)); + } + if (source.charAt(i) == '[') { + if (aiLatch) { + reduced.append('['); + } + lastAi = (10 * Character.getNumericValue(source.charAt(i + 1))) + + Character.getNumericValue(source.charAt(i + 2)); + aiLatch = ((lastAi < 0) || (lastAi > 4)) + && ((lastAi < 11) || (lastAi > 20)) + && (lastAi != 23) /* legacy support - see 5.3.8.2.2 */ + && ((lastAi < 31) || (lastAi > 36)) + && (lastAi != 41); + } + } + return reduced.toString(); + } + + private String hibcProcess(String source) { + char[] hibcCharTable = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', + '/', '+', '%'}; + + int counter, i; + String toProcess; + char checkDigit; + if (source.length() > 36) { + errorMsg = new StringBuilder("Data too long for HIBC LIC"); + return ""; + } + source = source.toUpperCase(); + if (!(source.matches("[A-Z0-9-\\. \\$/+\\%]+?"))) { + errorMsg = new StringBuilder("Invalid characters in input"); + return ""; + } + counter = 41; + for (i = 0; i < source.length(); i++) { + counter += positionOf(source.charAt(i), hibcCharTable); + } + counter = counter % 43; + if (counter < 10) { + checkDigit = (char) (counter + '0'); + } else { + if (counter < 36) { + checkDigit = (char) ((counter - 10) + 'A'); + } else { + switch (counter) { + case 36: + checkDigit = '-'; + break; + case 37: + checkDigit = '.'; + break; + case 38: + checkDigit = ' '; + break; + case 39: + checkDigit = '$'; + break; + case 40: + checkDigit = '/'; + break; + case 41: + checkDigit = '+'; + break; + case 42: + checkDigit = '%'; + break; + default: + checkDigit = ' '; + break; /* Keep compiler happy */ + } + } + } + encodeInfo.append("HIBC Check Digit: ").append(counter).append(" (").append(checkDigit).append(")\n"); + toProcess = "+" + source + checkDigit; + return toProcess; + } + + private static boolean roughlyEqual(double d1, double d2) { + return Math.abs(d1 - d2) < 0.0001; + } +} diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AustraliaPost.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AustraliaPost.java index 8ee8234..d224795 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AustraliaPost.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AustraliaPost.java @@ -6,7 +6,7 @@ import java.awt.geom.Rectangle2D; /** * Implements the Australia Post 4-State barcode. */ -public class AustraliaPost extends Symbol { +public class AustraliaPost extends AbstractSymbol { private static final char[] CHARACTER_SET = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', @@ -36,12 +36,11 @@ public class AustraliaPost extends Symbol { "301", "302", "303", "310", "311", "312", "313", "320", "321", "322", "323", "330", "331", "332", "333" }; - private ausMode mode; - ; + private Mode mode; public AustraliaPost() { - mode = ausMode.AUSPOST; + mode = Mode.AUSPOST; } /** @@ -102,7 +101,7 @@ public class AustraliaPost extends Symbol { * */ public void setPostMode() { - mode = ausMode.AUSPOST; + mode = Mode.AUSPOST; } /** @@ -110,7 +109,7 @@ public class AustraliaPost extends Symbol { * 4-State Barcode (FCC 45) which requires an 8-digit DPID input. */ public void setReplyMode() { - mode = ausMode.AUSREPLY; + mode = Mode.AUSREPLY; } /** @@ -118,7 +117,7 @@ public class AustraliaPost extends Symbol { * Barcode (FCC 87) which requires an 8-digit DPID input. */ public void setRouteMode() { - mode = ausMode.AUSROUTE; + mode = Mode.AUSROUTE; } /** @@ -126,7 +125,7 @@ public class AustraliaPost extends Symbol { * Barcode (FCC 92) which requires an 8-digit DPID input. */ public void setRedirectMode() { - mode = ausMode.AUSREDIRECT; + mode = Mode.AUSREDIRECT; } @Override @@ -196,7 +195,7 @@ public class AustraliaPost extends Symbol { encodeInfo.append("FCC: ").append(formatControlCode).append('\n'); - if (mode != ausMode.AUSPOST) { + if (mode != Mode.AUSPOST) { for (i = content.length(); i < 8; i++) { zeroPaddedInput.append("0"); } @@ -324,7 +323,7 @@ public class AustraliaPost extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock; int x, y, w, h; getRectangles().clear(); @@ -359,5 +358,20 @@ public class AustraliaPost extends Symbol { symbolHeight = 8; } - private enum ausMode {AUSPOST, AUSREPLY, AUSROUTE, AUSREDIRECT} + private enum Mode { + AUSPOST, AUSREPLY, AUSROUTE, AUSREDIRECT + } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.AUSTRALIA_POST; + } + + @Override + public AustraliaPost provide() { + return new AustraliaPost(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AztecCode.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AztecCode.java index bb81d3d..f83b92a 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AztecCode.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AztecCode.java @@ -9,7 +9,7 @@ import org.xbib.graphics.barcode.util.ReedSolomon; * 3000 alphabetic characters or 1900 bytes of data in a two-dimensional matrix * symbol. */ -public class AztecCode extends Symbol { +public class AztecCode extends AbstractSymbol { private static final int[] COMPACT_AZTEC_MAP = { //27 x 27 data grid 609, 608, 411, 413, 415, 417, 419, 421, 423, 425, 427, 429, 431, 433, @@ -586,7 +586,7 @@ public class AztecCode extends Symbol { eciProcess(); // Get ECI mode - if ((inputDataType == DataType.GS1) && (readerInit)) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (readerInit)) { errorMsg.append("Cannot encode in GS1 and Reader Initialisation mode at the same time"); return false; } @@ -1236,7 +1236,7 @@ public class AztecCode extends Symbol { /* Lookup input string in encoding table */ maplength = 0; - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { /* Add FNC1 to beginning of GS1 messages */ charmap[maplength] = 0; // FLG typemap[maplength++] = 8; // PUNC @@ -1277,7 +1277,7 @@ public class AztecCode extends Symbol { } for (i = 0; i < inputBytes.length; i++) { - if ((inputDataType == DataType.GS1) && ((inputBytes[i] & 0xFF) == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && ((inputBytes[i] & 0xFF) == '[')) { /* FNC1 represented by FLG(0) */ charmap[maplength] = 0; // FLG typemap[maplength++] = 8; // PUNC @@ -1986,4 +1986,17 @@ public class AztecCode extends Symbol { return binary.toString(); } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.AUSTRALIA_POST; + } + + @Override + public AztecCode provide() { + return new AztecCode(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AztecRune.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AztecRune.java index 2077831..baef2b9 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AztecRune.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/AztecRune.java @@ -8,7 +8,7 @@ import org.xbib.graphics.barcode.util.ReedSolomon; * Aztec Runes is a fixed-size matrix symbology which can encode whole * integer values between 0 and 255. */ -public class AztecRune extends Symbol { +public class AztecRune extends AbstractSymbol { private int[] bitPlacementMap = { 1, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, @@ -165,4 +165,17 @@ public class AztecRune extends Symbol { plotSymbol(); return true; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.AZTEC_RUNE; + } + + @Override + public AztecRune provide() { + return new AztecRune(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/ChannelCode.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/ChannelCode.java index ddd7301..9fb318c 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/ChannelCode.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/ChannelCode.java @@ -4,7 +4,7 @@ package org.xbib.graphics.barcode; * Implements Channel Code according to ANSI/AIM BC12-1998. * Channel code encodes whole integer values between 0 and 7,742,862. */ -public class ChannelCode extends Symbol { +public class ChannelCode extends AbstractSymbol { private int[] space = new int[11]; private int[] bar = new int[11]; private double currentValue; @@ -152,4 +152,17 @@ public class ChannelCode extends Symbol { } } } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CHANNEL_CODE; + } + + @Override + public ChannelCode provide() { + return new ChannelCode(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Codabar.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Codabar.java index 87aa226..56e65c0 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Codabar.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Codabar.java @@ -8,7 +8,7 @@ package org.xbib.graphics.barcode; * ($), colon (:), slash (/), full stop (.) or plus (+). No check digit is * generated. */ -public class Codabar extends Symbol { +public class Codabar extends AbstractSymbol { private static final String[] CODABAR_TABLE = { "11111221", "11112211", "11121121", "22111111", "11211211", @@ -83,4 +83,17 @@ public class Codabar extends Symbol { protected int[] getCodewords() { return getPatternAsCodewords(8); } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CODABAR; + } + + @Override + public Codabar provide() { + return new Codabar(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/CodablockF.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/CodablockF.java index a3df7e4..0e8b220 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/CodablockF.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/CodablockF.java @@ -9,10 +9,10 @@ import java.io.UnsupportedEncodingException; * encode any 8-bit ISO 8859-1 (Latin-1) data up to approximately 1000 * alpha-numeric characters or 2000 numeric digits in length. */ -public class CodablockF extends Symbol { +public class CodablockF extends AbstractSymbol { /* Annex A Table A.1 */ - private String[] C128Table = {"212222", "222122", "222221", "121223", "121322", "131222", "122213", + private final String[] c128Table = {"212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132", "122231", "113222", "123122", "123221", "223211", "221132", "221231", "213212", "223112", "312131", "311222", "321122", "321221", "312212", "322112", "322211", "212123", "212321", "232121", "111323", @@ -115,7 +115,7 @@ public class CodablockF extends Symbol { k1_sum = 0; k2_sum = 0; for (i = 0; i < input_length; i++) { - if ((inputDataType == DataType.GS1) && source[i] == '[') { + if ((inputSymbolDataType == SymbolDataType.GS1) && source[i] == '[') { k1_sum += (i + 1) * 29; /* GS */ k2_sum += i * 29; } else { @@ -208,35 +208,35 @@ public class CodablockF extends Symbol { row_pattern = new StringBuilder(); /* Start character */ - row_pattern.append(C128Table[103]); /* Always Start A */ + row_pattern.append(c128Table[103]); /* Always Start A */ switch (subset_selector[i]) { case MODEA: - row_pattern.append(C128Table[98]); + row_pattern.append(c128Table[98]); encodeInfo.append("MODEA "); break; case MODEB: - row_pattern.append(C128Table[100]); + row_pattern.append(c128Table[100]); encodeInfo.append("MODEB "); break; case MODEC: - row_pattern.append(C128Table[99]); + row_pattern.append(c128Table[99]); encodeInfo.append("MODEC "); break; } - row_pattern.append(C128Table[row_indicator[i]]); - encodeInfo.append(Integer.toString(row_indicator[i])).append(" "); + row_pattern.append(c128Table[row_indicator[i]]); + encodeInfo.append(row_indicator[i]).append(" "); for (j = 0; j < columns_needed; j++) { - row_pattern.append(C128Table[blockmatrix[i][j]]); - encodeInfo.append(Integer.toString(blockmatrix[i][j])).append(" "); + row_pattern.append(c128Table[blockmatrix[i][j]]); + encodeInfo.append(blockmatrix[i][j]).append(" "); } - row_pattern.append(C128Table[row_check[i]]); - encodeInfo.append("(").append(Integer.toString(row_check[i])).append(") "); + row_pattern.append(c128Table[row_check[i]]); + encodeInfo.append("(").append(row_check[i]).append(") "); /* Stop character */ - row_pattern.append(C128Table[106]); + row_pattern.append(c128Table[106]); /* Write the information into the symbol */ pattern[i] = row_pattern.toString(); @@ -293,7 +293,7 @@ public class CodablockF extends Symbol { c = columns_needed; current_mode = character_subset_select(input_position); subset_selector[current_row] = current_mode; - if ((current_row == 0) && (inputDataType == DataType.GS1)) { + if ((current_row == 0) && (inputSymbolDataType == SymbolDataType.GS1)) { /* Section 4.4.7.1 */ blockmatrix[current_row][column_position] = 102; /* FNC1 */ column_position++; @@ -301,7 +301,7 @@ public class CodablockF extends Symbol { } } - if ((inputDataType == DataType.GS1) && (source[input_position] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (source[input_position] == '[')) { blockmatrix[current_row][column_position] = 102; /* FNC1 */ column_position++; c--; @@ -487,7 +487,7 @@ public class CodablockF extends Symbol { if (!done) { if (((current_mode == cfMode.MODEA) || (current_mode == cfMode.MODEB)) && ((findSubset(source[input_position]) == Mode.ABORC) - || ((inputDataType == DataType.GS1) && (source[input_position] == '[')))) { + || ((inputSymbolDataType == SymbolDataType.GS1) && (source[input_position] == '[')))) { /* Count the number of numeric digits */ /* If 4 or more numeric data characters occur together when in subsets A or B: a. If there is an even number of numeric data characters, insert a Code C character before the @@ -498,12 +498,12 @@ public class CodablockF extends Symbol { j = 0; do { i++; - if ((inputDataType == DataType.GS1) && (source[input_position + j] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (source[input_position + j] == '[')) { i++; } j++; } while ((findSubset(source[input_position + j]) == Mode.ABORC) - || ((inputDataType == DataType.GS1) && (source[input_position + j] == '['))); + || ((inputSymbolDataType == SymbolDataType.GS1) && (source[input_position + j] == '['))); i--; if (i >= 4) { @@ -793,7 +793,7 @@ public class CodablockF extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock, yBlock; int x, y, w, h; boolean black; @@ -849,4 +849,17 @@ public class CodablockF extends Symbol { private enum cfMode { MODEA, MODEB, MODEC } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CODABLOCK_F; + } + + @Override + public CodablockF provide() { + return new CodablockF(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code11.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code11.java index b4e3dd1..1d1b1a2 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code11.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code11.java @@ -5,7 +5,7 @@ package org.xbib.graphics.barcode; * Code 11 can encode any length string consisting of the digits 0-9 and the * dash character (-). One or two modulo-11 check digits are calculated. */ -public class Code11 extends Symbol { +public class Code11 extends AbstractSymbol { private static final String[] CODE_11_TABLE = { "111121", "211121", "121121", "221111", "112121", "212111", @@ -205,4 +205,17 @@ public class Code11 extends Symbol { protected int[] getCodewords() { return getPatternAsCodewords(6); } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CODE11; + } + + @Override + public Code11 provide() { + return new Code11(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code128.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code128.java index 3272c45..4a92190 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code128.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code128.java @@ -7,7 +7,7 @@ import java.io.UnsupportedEncodingException; * Code 128 supports encoding of 8-bit ISO 8859-1 (Latin-1) characters. * Setting GS1 mode allows encoding in GS1-128 (also known as UPC/EAN-128). */ -public class Code128 extends Symbol { +public class Code128 extends AbstractSymbol { private String[] code128Table = { "212222", "222122", "222221", "121223", "121322", "131222", "122213", "122312", "132212", "221213", "221312", "231212", "112232", "122132", @@ -163,7 +163,7 @@ public class Code128 extends Symbol { mode_type[0] = mode; mode_length[0] = 1; - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { mode = Mode.ABORC; } @@ -174,7 +174,7 @@ public class Code128 extends Symbol { for (i = 1; i < sourcelen; i++) { last_mode = mode; mode = findSubset(inputData[i]); - if ((inputDataType == DataType.GS1) && inputData[i] == '[') { + if ((inputSymbolDataType == SymbolDataType.GS1) && inputData[i] == '[') { mode = Mode.ABORC; } if ((modeCSupression) && (mode == Mode.ABORC)) { @@ -193,7 +193,7 @@ public class Code128 extends Symbol { reduceSubsetChanges(); - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { /* Put set data into set[] */ read = 0; for (i = 0; i < index_point; i++) { @@ -323,7 +323,7 @@ public class Code128 extends Symbol { } if (set[i] == Mode.LATCHC) { - if ((inputDataType == DataType.GS1) && (inputData[i] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[i] == '[')) { glyph_count += 1.0; } else { glyph_count += 0.5; @@ -401,7 +401,7 @@ public class Code128 extends Symbol { } bar_characters++; - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { dest.append(code128Table[102]); values[1] = 102; bar_characters++; @@ -529,7 +529,7 @@ public class Code128 extends Symbol { bar_characters++; } - if (!((inputDataType == DataType.GS1) && (inputData[read] == '['))) { + if (!((inputSymbolDataType == SymbolDataType.GS1) && (inputData[read] == '['))) { /* Encode data characters */ c = inputData[read]; switch (set[read]) { @@ -655,11 +655,11 @@ public class Code128 extends Symbol { /* Stop character */ dest.append(code128Table[106]); - if (!(inputDataType == DataType.GS1)) { + if (!(inputSymbolDataType == SymbolDataType.GS1)) { readable = new StringBuilder(content); } - if (inputDataType == DataType.HIBC) { + if (inputSymbolDataType == SymbolDataType.HIBC) { readable.append("*").append(content).append("*"); } @@ -839,5 +839,20 @@ public class Code128 extends Symbol { SHIFTN, LATCHN, SHIFTF, LATCHF } - private enum Composite {OFF, CCA, CCB, CCC} + private enum Composite { + OFF, CCA, CCB, CCC + } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CODE128; + } + + @Override + public Code128 provide() { + return new Code128(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code16k.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code16k.java index eb2ae61..56776d3 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code16k.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code16k.java @@ -10,7 +10,7 @@ import java.io.UnsupportedEncodingException; * of any 8-bit ISO 8859-1 (Latin-1) data with a maximum data capacity of 77 * alpha-numeric characters or 154 numerical digits. */ -public class Code16k extends Symbol { +public class Code16k extends AbstractSymbol { /* EN 12323 Table 1 - "Code 16K" character encodations */ private static final String[] C_16_K_TABLE = { @@ -137,7 +137,7 @@ public class Code16k extends Symbol { indexchaine = 0; mode = findSubset(inputData[indexchaine]); - if ((inputDataType == DataType.GS1) && (inputData[indexchaine] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[indexchaine] == '[')) { mode = Mode.ABORC; } /* FNC1 */ @@ -152,7 +152,7 @@ public class Code16k extends Symbol { indexchaine++; if (indexchaine < input_length) { mode = findSubset(inputData[indexchaine]); - if ((inputDataType == DataType.GS1) && (inputData[indexchaine] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[indexchaine] == '[')) { mode = Mode.ABORC; } /* FNC1 */ } @@ -280,14 +280,14 @@ public class Code16k extends Symbol { } } - if ((set[i] == 'C') && (!((inputDataType == DataType.GS1) && (content.charAt(i) == '[')))) { + if ((set[i] == 'C') && (!((inputSymbolDataType == SymbolDataType.GS1) && (content.charAt(i) == '[')))) { glyph_count = glyph_count + 0.5; } else { glyph_count = glyph_count + 1.0; } } - if ((inputDataType == DataType.GS1) && (set[0] != 'A')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (set[0] != 'A')) { /* FNC1 can be integrated with mode character */ glyph_count--; } @@ -327,7 +327,7 @@ public class Code16k extends Symbol { if (m == 2) { m = 5; } - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { errorMsg.append("Cannot use both GS1 mode and Reader Initialisation"); return false; } else { @@ -339,7 +339,7 @@ public class Code16k extends Symbol { values[bar_characters + 1] = 96; /* FNC3 */ bar_characters += 2; } else { - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { /* Integrate FNC1 */ switch (set[0]) { case 'B': @@ -461,7 +461,7 @@ public class Code16k extends Symbol { bar_characters++; } - if (!((inputDataType == DataType.GS1) && (inputData[read] == '['))) { + if (!((inputSymbolDataType == SymbolDataType.GS1) && (inputData[read] == '['))) { switch (set[read]) { /* Encode data characters */ case 'A': case 'a': @@ -712,7 +712,7 @@ public class Code16k extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock, yBlock; int x, y, w, h; boolean black; @@ -766,4 +766,16 @@ public class Code16k extends Symbol { NULL, SHIFTA, LATCHA, SHIFTB, LATCHB, SHIFTC, LATCHC, AORB, ABORC, CANDB, CANDBB } + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CODE16K; + } + + @Override + public Code16k provide() { + return new Code16k(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code2Of5.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code2Of5.java index 6dba07d..7d3822a 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code2Of5.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code2Of5.java @@ -8,7 +8,7 @@ import java.awt.geom.Rectangle2D; /** * Implements the Code 2 of 5 family of barcode standards. */ -public class Code2Of5 extends Symbol { +public class Code2Of5 extends AbstractSymbol { private static final String[] C25_MATRIX_TABLE = { "113311", "311131", "131131", "331111", "113131", "313111", "133111", "111331", "311311", "131311" @@ -419,7 +419,7 @@ public class Code2Of5 extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock; getRectangles().clear(); getTexts().clear(); @@ -490,4 +490,17 @@ public class Code2Of5 extends Symbol { private enum ToFMode { MATRIX, INDUSTRIAL, IATA, DATA_LOGIC, INTERLEAVED, ITF14, DPLEIT, DPIDENT } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CODE25; + } + + @Override + public Code2Of5 provide() { + return new Code2Of5(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code32.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code32.java index 6c35717..7c6a8dd 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code32.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code32.java @@ -6,7 +6,7 @@ package org.xbib.graphics.barcode; * Requires a numeric input up to 8 digits in length. Check digit is * calculated. */ -public class Code32 extends Symbol { +public class Code32 extends AbstractSymbol { private char[] tabella = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', @@ -21,27 +21,22 @@ public class Code32 extends Symbol { StringBuilder risultante; int[] codeword = new int[6]; Code3Of9 c39 = new Code3Of9(); - if (content.length() > 8) { errorMsg.append("Input too long"); return false; } - if (!(content.matches("[0-9]+"))) { errorMsg.append("Invalid characters in input"); return false; } - /* Add leading zeros as required */ localstr = new StringBuilder(); for (i = content.length(); i < 8; i++) { localstr.append("0"); } localstr.append(content); - /* Calculate the check digit */ checksum = 0; - checkpart = 0; for (i = 0; i < 4; i++) { checkpart = Character.getNumericValue(localstr.charAt(i * 2)); checksum += checkpart; @@ -52,7 +47,6 @@ public class Code32 extends Symbol { checksum += checkpart; } } - /* Add check digit to data string */ checkdigit = checksum % 10; localstr.append((char) (checkdigit + '0')); @@ -100,4 +94,17 @@ public class Code32 extends Symbol { this.plotSymbol(); return true; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CODE32; + } + + @Override + public Code32 provide() { + return new Code32(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9.java index 7fb0378..b589e53 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9.java @@ -7,7 +7,7 @@ package org.xbib.graphics.barcode; * and percent (%). The standard does not require a check digit but a * modulo-43 check digit can be added if required. */ -public class Code3Of9 extends Symbol { +public class Code3Of9 extends AbstractSymbol { private static final String[] CODE_39 = { "1112212111", "2112111121", "1122111121", "2122111111", "1112211121", @@ -152,4 +152,17 @@ public class Code3Of9 extends Symbol { public enum CheckDigit { NONE, MOD43 } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CODE39; + } + + @Override + public Code3Of9 provide() { + return new Code3Of9(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9Extended.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9Extended.java index 30d9e63..988802a 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9Extended.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code3Of9Extended.java @@ -5,7 +5,7 @@ package org.xbib.graphics.barcode; * Supports encoding of all characters in the 7-bit ASCII table. A * modulo-43 check digit can be added if required. */ -public class Code3Of9Extended extends Symbol { +public class Code3Of9Extended extends AbstractSymbol { private final String[] ECode39 = { "%U", "$A", "$B", "$C", "$D", "$E", "$F", "$G", "$H", "$I", "$J", "$K", @@ -76,4 +76,17 @@ public class Code3Of9Extended extends Symbol { public enum CheckDigit { NONE, MOD43 } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CODE39_EXT; + } + + @Override + public Code3Of9Extended provide() { + return new Code3Of9Extended(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code49.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code49.java index ef55083..ffd7d26 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code49.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code49.java @@ -7,7 +7,7 @@ import java.awt.geom.Rectangle2D; * Encoding supports full 7-bit ASCII input up to a maximum of 49 characters * or 81 numeric digits. GS1 data encoding is also supported. */ -public class Code49 extends Symbol { +public class Code49 extends AbstractSymbol { private final String[] c49_table7 = { /* Table 7: Code 49 ASCII Chart */ @@ -988,7 +988,7 @@ public class Code49 extends Symbol { return false; } - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { intermediate.append("*"); // FNC1 } for (i = 0; i < length; i++) { @@ -996,7 +996,7 @@ public class Code49 extends Symbol { errorMsg.append("Invalid characters in input"); return false; } - if ((inputDataType == DataType.GS1) && (content.charAt(i) == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (content.charAt(i) == '[')) { intermediate.append("*"); // FNC1 } else { intermediate.append(c49_table7[content.charAt(i)]); @@ -1293,7 +1293,7 @@ public class Code49 extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock, yBlock; int x, y, w, h; boolean black; @@ -1342,4 +1342,17 @@ public class Code49 extends Symbol { symbolHeight += 2; mergeVerticalBlocks(); } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CODE49; + } + + @Override + public Code49 provide() { + return new Code49(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code93.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code93.java index a286650..77d3914 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code93.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Code93.java @@ -4,7 +4,7 @@ package org.xbib.graphics.barcode; * Implements Code 93. * Supports encoding of 7-bit ASCII text. Two check digits are added. */ -public class Code93 extends Symbol { +public class Code93 extends AbstractSymbol { /** * Code 93 control characters, indexed by ASCII codes (NOTE: a = Ctrl $, @@ -189,4 +189,17 @@ public class Code93 extends Symbol { protected int[] getCodewords() { return getPatternAsCodewords(6); } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CODE93; + } + + @Override + public Code93 provide() { + return new Code93(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/CodeOne.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/CodeOne.java index 78cb569..7f93938 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/CodeOne.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/CodeOne.java @@ -13,7 +13,7 @@ import java.math.BigInteger; * The width of version S and version T symbols is determined by the length * of the input data. */ -public class CodeOne extends Symbol { +public class CodeOne extends AbstractSymbol { private final int[] c40_shift = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -744,7 +744,7 @@ public class CodeOne extends Symbol { text_p = 0; edi_p = 0; - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { data[targetPoint] = 232; targetPoint++; } /* FNC1 */ @@ -840,7 +840,7 @@ public class CodeOne extends Symbol { } if (!(isTwoDigits)) { - if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (source[sourcePoint] == '[')) { if ((length - sourcePoint) >= 15) { /* Step B4 */ j = 0; @@ -903,7 +903,7 @@ public class CodeOne extends Symbol { sourcePoint++; } else { /* Step B8 */ - if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (source[sourcePoint] == '[')) { data[targetPoint] = 232; targetPoint++; sourcePoint++; /* FNC1 */ @@ -987,7 +987,7 @@ public class CodeOne extends Symbol { value = c40_value[source[sourcePoint]]; } - if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (source[sourcePoint] == '[')) { shift_set = 2; value = 27; /* FNC1 */ } @@ -1089,7 +1089,7 @@ public class CodeOne extends Symbol { value = text_value[source[sourcePoint]]; } - if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (source[sourcePoint] == '[')) { shift_set = 2; value = 27; /* FNC1 */ } @@ -1364,7 +1364,7 @@ public class CodeOne extends Symbol { if (current_mode == c1Mode.C1_BYTE) { next_mode = c1Mode.C1_BYTE; - if ((inputDataType == DataType.GS1) && (source[sourcePoint] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (source[sourcePoint] == '[')) { next_mode = c1Mode.C1_ASCII; } else { if (source[sourcePoint] <= 127) { @@ -1677,7 +1677,7 @@ public class CodeOne extends Symbol { } /* Step P */ - if ((inputDataType == DataType.GS1) && (source[sp] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (source[sp] == '[')) { byte_count += 3.0; } else { byte_count += 1.0; @@ -1943,4 +1943,17 @@ public class CodeOne extends Symbol { public enum Version { NONE, A, B, C, D, E, F, G, H, S, T } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.CODEONE; + } + + @Override + public CodeOne provide() { + return new CodeOne(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Composite.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Composite.java index fd0ef27..32d6bed 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Composite.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Composite.java @@ -12,7 +12,7 @@ import java.util.List; * and a "linear" element which can be UPC, EAN, Code 128 or * GS1 DataBar symbol. */ -public class Composite extends Symbol { +public class Composite extends AbstractSymbol { /* CC-A component coefficients from ISO/IEC 24728:2006 Annex F */ private int[] ccaCoeffs = { /* k = 4 */ @@ -435,11 +435,11 @@ public class Composite extends Symbol { private int linearWidth; // Width of Code 128 linear public Composite() { - inputDataType = DataType.GS1; + inputSymbolDataType = SymbolDataType.GS1; } @Override - public void setDataType(DataType dummy) { + public void setDataType(SymbolDataType dummy) { // Do nothing! } @@ -555,7 +555,7 @@ public class Composite extends Symbol { bottomShift = 7; break; } - code128.setDataType(DataType.GS1); + code128.setDataType(SymbolDataType.GS1); code128.setContent(linearContent); linearWidth = code128.symbolWidth; linearRect = code128.getRectangles(); @@ -2843,4 +2843,17 @@ public class Composite extends Symbol { public enum CompositeMode { CC_A, CC_B, CC_C } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.COMPOSITE; + } + + @Override + public Composite provide() { + return new Composite(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataBar14.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataBar14.java index c553eb7..5020bc2 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataBar14.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataBar14.java @@ -7,7 +7,7 @@ import java.math.BigInteger; * Input data should be a 13 digit Global Trade Identification Number * without check digit or Application Identifier [01]. */ -public class DataBar14 extends Symbol { +public class DataBar14 extends AbstractSymbol { private int[] g_sum_table = { 0, 161, 961, 2015, 2715, 0, 336, 1036, 1516 @@ -49,7 +49,7 @@ public class DataBar14 extends Symbol { } @Override - public void setDataType(DataType dummy) { + public void setDataType(SymbolDataType dummy) { // Do nothing! } @@ -695,4 +695,17 @@ public class DataBar14 extends Symbol { private enum gb14Mode { LINEAR, OMNI, STACKED } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.DATABAR_14; + } + + @Override + public DataBar14 provide() { + return new DataBar14(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataBarExpanded.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataBarExpanded.java index fe0da9d..196b7cc 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataBarExpanded.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataBarExpanded.java @@ -6,7 +6,7 @@ package org.xbib.graphics.barcode; * DataBar expanded encodes GS1 data in either a linear or stacked * format. */ -public class DataBarExpanded extends Symbol { +public class DataBarExpanded extends AbstractSymbol { private static final int[] G_SUM_EXP = { 0, 348, 1388, 2948, 3988 @@ -77,7 +77,7 @@ public class DataBarExpanded extends Symbol { public DataBarExpanded() { linkageFlag = false; - inputDataType = DataType.GS1; + inputSymbolDataType = SymbolDataType.GS1; } private static int calculateRemainder(int binaryStringLength) { @@ -104,7 +104,7 @@ public class DataBarExpanded extends Symbol { ; @Override - public void setDataType(DataType dummy) { + public void setDataType(SymbolDataType dummy) { // Do nothing! } @@ -1649,4 +1649,17 @@ public class DataBarExpanded extends Symbol { private enum EncodeMode { NUMERIC, ALPHA, ISOIEC, INVALID_CHAR, ANY_ENC, ALPHA_OR_ISO } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.DATABAR_EXPANDED; + } + + @Override + public DataBarExpanded provide() { + return new DataBarExpanded(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataBarLimited.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataBarLimited.java index 4163543..f81541c 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataBarLimited.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataBarLimited.java @@ -7,7 +7,7 @@ import java.math.BigInteger; * Input data should be a 12 digit Global Trade Identification Number * without check digit or Application Identifier [01]. */ -public class DataBarLimited extends Symbol { +public class DataBarLimited extends AbstractSymbol { private static final int[] t_even_ltd = { 28, 728, 6454, 203, 2408, 1, 16632 @@ -128,7 +128,7 @@ public class DataBarLimited extends Symbol { } @Override - public void setDataType(DataType dummy) { + public void setDataType(SymbolDataType dummy) { // Do nothing! } @@ -473,4 +473,17 @@ public class DataBarLimited extends Symbol { } widths[bar] = n; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.DATABAR_LIMITED; + } + + @Override + public DataBarLimited provide() { + return new DataBarLimited(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataMatrix.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataMatrix.java index 774db2a..e29ee0a 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataMatrix.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/DataMatrix.java @@ -8,7 +8,7 @@ import org.xbib.graphics.barcode.util.ReedSolomon; * Data Matrix is a 2D matrix symbology capable of encoding characters in the * ISO/IEC 8859-1 (Latin-1) character set. */ -public class DataMatrix extends Symbol { +public class DataMatrix extends AbstractSymbol { private static final int[] c40_shift = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -90,7 +90,7 @@ public class DataMatrix extends Symbol { private int[] target = new int[2200]; private int[] binary = new int[2200]; private int binary_length; - private dm_mode last_mode; + private Mode last_mode; private int[] places; private boolean isSquare; private int[] inputData; @@ -441,7 +441,7 @@ public class DataMatrix extends Symbol { encodeInfo.append("Encoding: "); int sp, tp, i; - dm_mode current_mode, next_mode; + Mode current_mode, next_mode; int inputlen = content.length(); sp = 0; @@ -454,10 +454,10 @@ public class DataMatrix extends Symbol { binary_length = 0; /* step (a) */ - current_mode = dm_mode.DM_ASCII; - next_mode = dm_mode.DM_ASCII; + current_mode = Mode.DM_ASCII; + next_mode = Mode.DM_ASCII; - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { target[tp] = 232; tp++; binary[binary_length] = ' '; @@ -494,7 +494,7 @@ public class DataMatrix extends Symbol { } if (readerInit) { - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { errorMsg.append("Cannot encode in GS1 mode and Reader Initialisation at the same time"); return 0; } else { @@ -539,8 +539,8 @@ public class DataMatrix extends Symbol { current_mode = next_mode; /* step (b) - ASCII encodation */ - if (current_mode == dm_mode.DM_ASCII) { - next_mode = dm_mode.DM_ASCII; + if (current_mode == Mode.DM_ASCII) { + next_mode = Mode.DM_ASCII; for (i = 0; i < 8; i++) { process_buffer[i] = 0; @@ -557,7 +557,7 @@ public class DataMatrix extends Symbol { } else { next_mode = lookAheadTest(sp, current_mode); - if (next_mode != dm_mode.DM_ASCII) { + if (next_mode != Mode.DM_ASCII) { switch (next_mode) { case DM_C40: target[tp] = 230; @@ -609,7 +609,7 @@ public class DataMatrix extends Symbol { binary[binary_length] = ' '; binary_length++; } else { - if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[sp] == '[')) { target[tp] = 232; /* FNC1 */ encodeInfo.append("FNC1 "); } else { @@ -627,21 +627,21 @@ public class DataMatrix extends Symbol { } /* step (c) C40 encodation */ - if (current_mode == dm_mode.DM_C40) { + if (current_mode == Mode.DM_C40) { int shift_set, value; - next_mode = dm_mode.DM_C40; + next_mode = Mode.DM_C40; if (process_p == 0) { next_mode = lookAheadTest(sp, current_mode); } - if (next_mode != dm_mode.DM_C40) { + if (next_mode != Mode.DM_C40) { target[tp] = 254; tp++; binary[binary_length] = ' '; binary_length++; /* Unlatch */ - next_mode = dm_mode.DM_ASCII; + next_mode = Mode.DM_ASCII; encodeInfo.append("ASC "); } else { if (inputData[sp] > 127) { @@ -657,7 +657,7 @@ public class DataMatrix extends Symbol { value = c40_value[inputData[sp]]; } - if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[sp] == '[')) { shift_set = 2; value = 27; /* FNC1 */ @@ -701,21 +701,21 @@ public class DataMatrix extends Symbol { } /* step (d) Text encodation */ - if (current_mode == dm_mode.DM_TEXT) { + if (current_mode == Mode.DM_TEXT) { int shift_set, value; - next_mode = dm_mode.DM_TEXT; + next_mode = Mode.DM_TEXT; if (process_p == 0) { next_mode = lookAheadTest(sp, current_mode); } - if (next_mode != dm_mode.DM_TEXT) { + if (next_mode != Mode.DM_TEXT) { target[tp] = 254; tp++; binary[binary_length] = ' '; binary_length++; /* Unlatch */ - next_mode = dm_mode.DM_ASCII; + next_mode = Mode.DM_ASCII; encodeInfo.append("ASC "); } else { if (inputData[sp] > 127) { @@ -731,7 +731,7 @@ public class DataMatrix extends Symbol { value = text_value[inputData[sp]]; } - if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[sp] == '[')) { shift_set = 2; value = 27; /* FNC1 */ @@ -774,21 +774,21 @@ public class DataMatrix extends Symbol { } /* step (e) X12 encodation */ - if (current_mode == dm_mode.DM_X12) { + if (current_mode == Mode.DM_X12) { int value = 0; - next_mode = dm_mode.DM_X12; + next_mode = Mode.DM_X12; if (process_p == 0) { next_mode = lookAheadTest(sp, current_mode); } - if (next_mode != dm_mode.DM_X12) { + if (next_mode != Mode.DM_X12) { target[tp] = 254; tp++; binary[binary_length] = ' '; binary_length++; /* Unlatch */ - next_mode = dm_mode.DM_ASCII; + next_mode = Mode.DM_ASCII; encodeInfo.append("ASC "); } else { if (inputData[sp] == 13) { @@ -843,18 +843,18 @@ public class DataMatrix extends Symbol { } /* step (f) EDIFACT encodation */ - if (current_mode == dm_mode.DM_EDIFACT) { + if (current_mode == Mode.DM_EDIFACT) { int value = 0; - next_mode = dm_mode.DM_EDIFACT; + next_mode = Mode.DM_EDIFACT; if (process_p == 3) { next_mode = lookAheadTest(sp, current_mode); } - if (next_mode != dm_mode.DM_EDIFACT) { + if (next_mode != Mode.DM_EDIFACT) { process_buffer[process_p] = 31; process_p++; - next_mode = dm_mode.DM_ASCII; + next_mode = Mode.DM_ASCII; } else { if ((inputData[sp] >= '@') && (inputData[sp] <= '^')) { value = inputData[sp] - '@'; @@ -901,10 +901,10 @@ public class DataMatrix extends Symbol { } /* step (g) Base 256 encodation */ - if (current_mode == dm_mode.DM_BASE256) { + if (current_mode == Mode.DM_BASE256) { next_mode = lookAheadTest(sp, current_mode); - if (next_mode == dm_mode.DM_BASE256) { + if (next_mode == Mode.DM_BASE256) { target[tp] = inputData[sp]; encodeInfo.append(Integer.toString(target[tp])).append(" "); tp++; @@ -912,7 +912,7 @@ public class DataMatrix extends Symbol { binary[binary_length] = 'b'; binary_length++; } else { - next_mode = dm_mode.DM_ASCII; + next_mode = Mode.DM_ASCII; encodeInfo.append("ASC "); } } @@ -1133,16 +1133,16 @@ public class DataMatrix extends Symbol { return false; } - private dm_mode lookAheadTest(int position, dm_mode current_mode) { + private Mode lookAheadTest(int position, Mode current_mode) { /* 'look ahead test' from Annex P */ double ascii_count, c40_count, text_count, x12_count, edf_count, b256_count, best_count; int sp; int sourcelen = content.length(); - dm_mode best_scheme = dm_mode.NULL; + Mode best_scheme = Mode.NULL; /* step (j) */ - if (current_mode == dm_mode.DM_ASCII) { + if (current_mode == Mode.DM_ASCII) { ascii_count = 0.0; c40_count = 1.0; text_count = 1.0; @@ -1189,30 +1189,30 @@ public class DataMatrix extends Symbol { c40_count = Math.ceil(c40_count); best_count = c40_count; - best_scheme = dm_mode.DM_C40; // (k)(7) + best_scheme = Mode.DM_C40; // (k)(7) if (x12_count < best_count) { best_count = x12_count; - best_scheme = dm_mode.DM_X12; // (k)(6) + best_scheme = Mode.DM_X12; // (k)(6) } if (text_count < best_count) { best_count = text_count; - best_scheme = dm_mode.DM_TEXT; // (k)(5) + best_scheme = Mode.DM_TEXT; // (k)(5) } if (edf_count < best_count) { best_count = edf_count; - best_scheme = dm_mode.DM_EDIFACT; // (k)(4) + best_scheme = Mode.DM_EDIFACT; // (k)(4) } if (b256_count < best_count) { best_count = b256_count; - best_scheme = dm_mode.DM_BASE256; // (k)(3) + best_scheme = Mode.DM_BASE256; // (k)(3) } if (ascii_count <= best_count) { - best_scheme = dm_mode.DM_ASCII; // (k)(2) + best_scheme = Mode.DM_ASCII; // (k)(2) } } else { @@ -1274,12 +1274,12 @@ public class DataMatrix extends Symbol { edf_count += (13.0 / 4.0); // (p)(3) } } - if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[sp] == '[')) { edf_count += 6.0; } /* base 256 ... step (q) */ - if ((inputDataType == DataType.GS1) && (inputData[sp] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[sp] == '[')) { b256_count += 4.0; // (q)(1) } else { b256_count += 1.0; // (q)(2) @@ -1297,15 +1297,15 @@ public class DataMatrix extends Symbol { ((c40_count + 1.0) < text_count)) { if (c40_count < x12_count) { - best_scheme = dm_mode.DM_C40; + best_scheme = Mode.DM_C40; } if (c40_count == x12_count) { if (p_r_6_2_1(sp, sourcelen)) { // Test (r)(6)(ii)(i) - best_scheme = dm_mode.DM_X12; + best_scheme = Mode.DM_X12; } else { - best_scheme = dm_mode.DM_C40; + best_scheme = Mode.DM_C40; } } } @@ -1316,7 +1316,7 @@ public class DataMatrix extends Symbol { ((x12_count + 1.0) < edf_count) && ((x12_count + 1.0) < text_count) && ((x12_count + 1.0) < c40_count)) { - best_scheme = dm_mode.DM_X12; + best_scheme = Mode.DM_X12; } /* step (r)(4) */ @@ -1325,7 +1325,7 @@ public class DataMatrix extends Symbol { ((text_count + 1.0) < edf_count) && ((text_count + 1.0) < x12_count) && ((text_count + 1.0) < c40_count)) { - best_scheme = dm_mode.DM_TEXT; + best_scheme = Mode.DM_TEXT; } /* step (r)(3) */ @@ -1334,7 +1334,7 @@ public class DataMatrix extends Symbol { ((edf_count + 1.0) < text_count) && ((edf_count + 1.0) < x12_count) && ((edf_count + 1.0) < c40_count)) { - best_scheme = dm_mode.DM_EDIFACT; + best_scheme = Mode.DM_EDIFACT; } /* step (r)(2) */ @@ -1343,7 +1343,7 @@ public class DataMatrix extends Symbol { ((b256_count + 1.0) < text_count) && ((b256_count + 1.0) < x12_count) && ((b256_count + 1.0) < c40_count))) { - best_scheme = dm_mode.DM_BASE256; + best_scheme = Mode.DM_BASE256; } /* step (r)(1) */ @@ -1352,12 +1352,12 @@ public class DataMatrix extends Symbol { ((ascii_count + 1.0) <= text_count) && ((ascii_count + 1.0) <= x12_count) && ((ascii_count + 1.0) <= c40_count)) { - best_scheme = dm_mode.DM_ASCII; + best_scheme = Mode.DM_ASCII; } } sp++; - } while (best_scheme == dm_mode.NULL); // step (s) + } while (best_scheme == Mode.NULL); // step (s) return best_scheme; } @@ -1629,8 +1629,21 @@ public class DataMatrix extends Symbol { places[r * NC + c] = (p << 3) + b; } - private enum dm_mode { + private enum Mode { NULL, DM_ASCII, DM_C40, DM_TEXT, DM_X12, DM_EDIFACT, DM_BASE256 } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.DATA_MATRIX; + } + + @Override + public DataMatrix provide() { + return new DataMatrix(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Ean.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Ean.java index d378a06..ef8d4c2 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Ean.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Ean.java @@ -14,7 +14,7 @@ import java.awt.geom.Rectangle2D; * generally for internal use only. Check digit is calculated and should not * be in input data. Leading zeroes are added as required. */ -public class Ean extends Symbol { +public class Ean extends AbstractSymbol { private boolean useAddOn; @@ -244,7 +244,7 @@ public class Ean extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock; int x, y, w, h; boolean black; @@ -363,4 +363,17 @@ public class Ean extends Symbol { public enum Mode { EAN8, EAN13 } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.EAN; + } + + @Override + public Ean provide() { + return new Ean(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/GridMatrix.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/GridMatrix.java index 59835e0..95afa0a 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/GridMatrix.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/GridMatrix.java @@ -9,7 +9,7 @@ import java.io.UnsupportedEncodingException; * 8859-1 (Latin-1) character set as well as those in the GB-2312 character set. * Input is assumed to be formatted as a UTF string. */ -public class GridMatrix extends Symbol { +public class GridMatrix extends AbstractSymbol { private final char[] shift_set = { /* From Table 7 - Encoding of control characters */ @@ -2012,4 +2012,17 @@ public class GridMatrix extends Symbol { NULL, GM_NUMBER, GM_LOWER, GM_UPPER, GM_MIXED, GM_CONTROL, GM_BYTE, GM_CHINESE } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.GRID_MATRIX; + } + + @Override + public GridMatrix provide() { + return new GridMatrix(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/JapanPost.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/JapanPost.java index ebfab19..9928ba0 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/JapanPost.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/JapanPost.java @@ -9,7 +9,7 @@ import java.util.Locale; * characters A-Z and the dash (-) character. A modulo-19 check digit is * added and should not be included in the input data. */ -public class JapanPost extends Symbol { +public class JapanPost extends AbstractSymbol { private static final String[] JAPAN_TABLE = { "FFT", "FDA", "DFA", "FAD", "FTF", "DAF", "AFD", "ADF", "TFF", "FTT", @@ -101,7 +101,7 @@ public class JapanPost extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock; int x, y, w, h; getRectangles().clear(); @@ -135,4 +135,17 @@ public class JapanPost extends Symbol { symbolWidth = pattern[0].length() * 3; symbolHeight = 8; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.JAPAN_POST; + } + + @Override + public JapanPost provide() { + return new JapanPost(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/KixCode.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/KixCode.java index faecb34..be6589c 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/KixCode.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/KixCode.java @@ -8,7 +8,7 @@ import java.util.Locale; * (Netherlands). Input data can consist of digits 0-9 and characters A-Z. * Input should be 11 characters in length. No check digit is added. */ -public class KixCode extends Symbol { +public class KixCode extends AbstractSymbol { /* Handles Dutch Post TNT KIX symbols */ /* The same as RM4SCC but without check digit */ @@ -58,7 +58,7 @@ public class KixCode extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock; int x, y, w, h; getRectangles().clear(); @@ -92,4 +92,17 @@ public class KixCode extends Symbol { symbolWidth = pattern[0].length() * 3; symbolHeight = 8; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.KIX_CODE; + } + + @Override + public KixCode provide() { + return new KixCode(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/KoreaPost.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/KoreaPost.java index 6ca94e3..9226817 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/KoreaPost.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/KoreaPost.java @@ -5,7 +5,7 @@ package org.xbib.graphics.barcode; * number. A Modulo-10 check digit is calculated and added, and should not form * part of the input data. */ -public class KoreaPost extends Symbol { +public class KoreaPost extends AbstractSymbol { String[] koreaTable = { "1313150613", "0713131313", "0417131313", "1506131313", "0413171313", @@ -58,4 +58,17 @@ public class KoreaPost extends Symbol { plotSymbol(); return true; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.KOREA_POST; + } + + @Override + public KoreaPost provide() { + return new KoreaPost(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Logmars.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Logmars.java index 71ea571..df7bca3 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Logmars.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Logmars.java @@ -8,7 +8,7 @@ package org.xbib.graphics.barcode; * A Modulo-43 check digit is calculated and added, and should not form part * of the input data. */ -public class Logmars extends Symbol { +public class Logmars extends AbstractSymbol { private static final String[] CODE39LM = { "1113313111", "3113111131", "1133111131", "3133111111", "1113311131", @@ -94,4 +94,17 @@ public class Logmars extends Symbol { plotSymbol(); return true; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.LOG_MARS; + } + + @Override + public Logmars provide() { + return new Logmars(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/MaxiCode.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/MaxiCode.java index e90db8a..4eb1636 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/MaxiCode.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/MaxiCode.java @@ -13,7 +13,7 @@ import java.util.Arrays; * any from the ISO 8859-1 (Latin-1) character set. * TODO: Add ECI functionality. */ -public class MaxiCode extends Symbol { +public class MaxiCode extends AbstractSymbol { /** * MaxiCode module sequence, from ISO/IEC 16023 Figure 5 (30 x 33 data grid). @@ -851,8 +851,7 @@ public class MaxiCode extends Symbol { } @Override - protected void plotSymbol() { - + public void plotSymbol() { // hexagons for (int row = 0; row < 33; row++) { for (int col = 0; col < 30; col++) { @@ -866,7 +865,6 @@ public class MaxiCode extends Symbol { } } } - // circles double[] radii = {10.85, 8.97, 7.10, 5.22, 3.31, 1.43}; for (double aRadii : radii) { @@ -880,4 +878,17 @@ public class MaxiCode extends Symbol { protected int[] getCodewords() { return codewords; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.MAXI_CODE; + } + + @Override + public MaxiCode provide() { + return new MaxiCode(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/MicroQrCode.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/MicroQrCode.java index a66698c..aad79ea 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/MicroQrCode.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/MicroQrCode.java @@ -10,7 +10,7 @@ import java.io.UnsupportedEncodingException; * QR Code symbols can encode characters in the Latin-1 set and Kanji * characters which are members of the Shift-JIS encoding scheme. */ -public class MicroQrCode extends Symbol { +public class MicroQrCode extends AbstractSymbol { /* Table 5 - Encoding/Decoding table for Alphanumeric mode */ private static final char[] RHODIUM = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', @@ -1590,4 +1590,17 @@ public class MicroQrCode extends Symbol { public enum EccMode { L, M, Q, H } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.MICRO_QRCODE; + } + + @Override + public MicroQrCode provide() { + return new MicroQrCode(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/MsiPlessey.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/MsiPlessey.java index 45a6f8d..13c5119 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/MsiPlessey.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/MsiPlessey.java @@ -5,7 +5,7 @@ package org.xbib.graphics.barcode; * MSI Plessey can encode a string of numeric digits and has a range * of check digit options. */ -public class MsiPlessey extends Symbol { +public class MsiPlessey extends AbstractSymbol { private final String[] MSI_PlessTable = { "12121212", "12121221", "12122112", "12122121", "12211212", "12211221", @@ -185,4 +185,17 @@ public class MsiPlessey extends Symbol { public enum CheckDigit { NONE, MOD10, MOD10_MOD10, MOD11, MOD11_MOD10 } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.MSI_PLESSEY; + } + + @Override + public MsiPlessey provide() { + return new MsiPlessey(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Nve18.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Nve18.java index 021f732..057e942 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Nve18.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Nve18.java @@ -7,7 +7,7 @@ import java.util.stream.IntStream; * Also SSCC-18 (Serial Shipping Container Code) * Encodes a 17 digit number, adding a Modulo-10 check digit. */ -public class Nve18 extends Symbol { +public class Nve18 extends AbstractSymbol { @Override public boolean encode() { @@ -51,7 +51,7 @@ public class Nve18 extends Symbol { content = "[00]" + gs1Equivalent + cdigit; // Defer to Code 128 - code128.setDataType(DataType.GS1); + code128.setDataType(SymbolDataType.GS1); code128.setHumanReadableLocation(getHumanReadableLocation()); try { @@ -69,4 +69,17 @@ public class Nve18 extends Symbol { encodeInfo.append(code128.encodeInfo); return true; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.NVE_18; + } + + @Override + public Nve18 provide() { + return new Nve18(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pdf417.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pdf417.java index 8e72bc9..126d8ac 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pdf417.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pdf417.java @@ -12,7 +12,7 @@ import java.util.List; * of 1850 text characters, or 2710 digits. The maximum size MicroPDF417 symbol * can hold 250 alphanumeric characters or 366 digits. */ -public class Pdf417 extends Symbol { +public class Pdf417 extends AbstractSymbol { private static final int MAX_NUMERIC_COMPACTION_BLOCK_SIZE = 44; @@ -1701,4 +1701,17 @@ public class Pdf417 extends Symbol { return mode + "x" + length; } } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.PDF417; + } + + @Override + public Pdf417 provide() { + return new Pdf417(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode.java index 573490a..1cc3682 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode.java @@ -6,7 +6,7 @@ package org.xbib.graphics.barcode; * Pharmacode is used for the identification of pharmaceuticals. The symbology * is able to encode whole numbers between 3 and 131070. */ -public class Pharmacode extends Symbol { +public class Pharmacode extends AbstractSymbol { @Override public boolean encode() { @@ -63,4 +63,17 @@ public class Pharmacode extends Symbol { plotSymbol(); return true; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.PHARMACODE; + } + + @Override + public Pharmacode provide() { + return new Pharmacode(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode2Track.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode2Track.java index 6be57f6..ace9f5a 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode2Track.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pharmacode2Track.java @@ -8,7 +8,7 @@ import java.awt.geom.Rectangle2D; * for the identification of pharmaceuticals. The symbology is able to encode * whole numbers between 4 and 64570080. */ -public class Pharmacode2Track extends Symbol { +public class Pharmacode2Track extends AbstractSymbol { @Override public boolean encode() { @@ -73,7 +73,7 @@ public class Pharmacode2Track extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock; int x, y, w, h; getRectangles().clear(); @@ -103,4 +103,17 @@ public class Pharmacode2Track extends Symbol { symbolWidth = pattern[0].length() * 2; symbolHeight = defaultHeight; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.PHARMACODE2TRACK; + } + + @Override + public Pharmacode2Track provide() { + return new Pharmacode2Track(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pharmazentralnummer.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pharmazentralnummer.java index 6e3bcad..d8e66b1 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pharmazentralnummer.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Pharmazentralnummer.java @@ -4,7 +4,7 @@ package org.xbib.graphics.barcode; * PZN8 is a Code 39 based symbology used by the pharmaceutical industry in * Germany. PZN8 encodes a 7 digit number and includes a modulo-10 check digit. */ -public class Pharmazentralnummer extends Symbol { +public class Pharmazentralnummer extends AbstractSymbol { /* Pharmazentral Nummer is a Code 3 of 9 symbol with an extra * check digit. Now generates PZN-8. @@ -66,4 +66,17 @@ public class Pharmazentralnummer extends Symbol { plotSymbol(); return true; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.PHARMAZENTRALNUMMER; + } + + @Override + public Pharmazentralnummer provide() { + return new Pharmazentralnummer(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Postnet.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Postnet.java index eade347..d2c2329 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Postnet.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Postnet.java @@ -12,16 +12,16 @@ import java.awt.geom.Rectangle2D; * POSTNET and PLANET both use numerical input data and include a modulo-10 * check digit. */ -public class Postnet extends Symbol { +public class Postnet extends AbstractSymbol { private static final String[] PN_TABLE = { "LLSSS", "SSSLL", "SSLSL", "SSLLS", "SLSSL", "SLSLS", "SLLSS", "LSSSL", "LSSLS", "LSLSS" }; - ; private static final String[] PL_TABLE = { "SSLLL", "LLLSS", "LLSLS", "LLSSL", "LSLLS", "LSLSL", "LSSLL", "SLLLS", "SLLSL", "SLSLL" }; + private Mode mode; public Postnet() { @@ -140,7 +140,7 @@ public class Postnet extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock, shortHeight; double x, y, w, h; getRectangles().clear(); @@ -182,4 +182,17 @@ public class Postnet extends Symbol { public enum Mode { PLANET, POSTNET } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.POSTNET; + } + + @Override + public Postnet provide() { + return new Postnet(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/QrCode.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/QrCode.java index a528ddf..0abb5b1 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/QrCode.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/QrCode.java @@ -11,7 +11,7 @@ import java.io.UnsupportedEncodingException; * Latin-1 set and Kanji characters which are members of the Shift-JIS encoding * scheme. */ -public class QrCode extends Symbol { +public class QrCode extends AbstractSymbol { /* Table 5 - Encoding/Decoding table for Alphanumeric mode */ private final char[] rhodium = { @@ -658,7 +658,7 @@ public class QrCode extends Symbol { if (isXAlpha((char) (inputData[i] & 0xFF))) { inputMode[i] = qrMode.ALPHANUM; } - if ((inputDataType == DataType.GS1) && (inputData[i] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[i] == '[')) { inputMode[i] = qrMode.ALPHANUM; } if (isXNumeric((char) (inputData[i] & 0xff))) { @@ -682,7 +682,7 @@ public class QrCode extends Symbol { count += 12; } - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { count += 4; } @@ -924,7 +924,7 @@ public class QrCode extends Symbol { count += 12; } - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { count += 4; } @@ -1031,7 +1031,7 @@ public class QrCode extends Symbol { } } - if (inputDataType == DataType.GS1) { + if (inputSymbolDataType == SymbolDataType.GS1) { binary.append("0101"); /* FNC1 */ } @@ -1137,7 +1137,7 @@ public class QrCode extends Symbol { // Process 8-bit byte int lbyte = (inputData[position + i] & 0xFF); - if ((inputDataType == DataType.GS1) && (lbyte == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (lbyte == '[')) { lbyte = 0x1d; /* FNC1 */ } @@ -1164,14 +1164,14 @@ public class QrCode extends Symbol { while (i < short_data_block_length) { if (!alphanumPercent) { - if ((inputDataType == DataType.GS1) && (inputData[position + i] == '%')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[position + i] == '%')) { first = positionOf('%', rhodium); second = positionOf('%', rhodium); count = 2; prod = (first * 45) + second; i++; } else { - if ((inputDataType == DataType.GS1) && (inputData[position + i] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[position + i] == '[')) { first = positionOf('%', rhodium); /* FNC1 */ } else { @@ -1183,13 +1183,13 @@ public class QrCode extends Symbol { if (i < short_data_block_length) { if (inputMode[position + i] == qrMode.ALPHANUM) { - if ((inputDataType == DataType.GS1) && (inputData[position + i] == '%')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[position + i] == '%')) { second = positionOf('%', rhodium); count = 2; prod = (first * 45) + second; alphanumPercent = true; } else { - if ((inputDataType == DataType.GS1) && (inputData[position + i] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[position + i] == '[')) { second = positionOf('%', rhodium); /* FNC1 */ } else { @@ -1211,13 +1211,13 @@ public class QrCode extends Symbol { if (i < short_data_block_length) { if (inputMode[position + i] == qrMode.ALPHANUM) { - if ((inputDataType == DataType.GS1) && (inputData[position + i] == '%')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[position + i] == '%')) { second = positionOf('%', rhodium); count = 2; prod = (first * 45) + second; alphanumPercent = true; } else { - if ((inputDataType == DataType.GS1) && (inputData[position + i] == '[')) { + if ((inputSymbolDataType == SymbolDataType.GS1) && (inputData[position + i] == '[')) { second = positionOf('%', rhodium); /* FNC1 */ } else { @@ -2005,4 +2005,17 @@ public class QrCode extends Symbol { L, M, Q, H } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.QRCODE; + } + + @Override + public QrCode provide() { + return new QrCode(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/RoyalMail4State.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/RoyalMail4State.java index 9fddf90..8057caf 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/RoyalMail4State.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/RoyalMail4State.java @@ -9,10 +9,10 @@ import java.util.Locale; * delivery postcode followed by house number. A check digit is calculated * and added. */ -public class RoyalMail4State extends Symbol { +public class RoyalMail4State extends AbstractSymbol { /* Handles the 4 State barcodes used in the UK by Royal Mail */ - private String[] RoyalTable = { + private final String[] royalTable = { "TTFF", "TDAF", "TDFA", "DTAF", "DTFA", "DDAA", "TADF", "TFTF", "TFDA", "DATF", "DADA", "DFTA", "TAFD", "TFAD", "TFFT", "DAAD", "DAFT", "DFAT", "ATDF", "ADTF", "ADDA", "FTTF", "FTDA", "FDTA", "ATFD", "ADAD", "ADFT", @@ -41,7 +41,7 @@ public class RoyalMail4State extends Symbol { for (i = 0; i < content.length(); i++) { index = positionOf(content.charAt(i), krSet); - dest.append(RoyalTable[index]); + dest.append(royalTable[index]); top += (index + 1) % 6; bottom += ((index / 6) + 1) % 6; } @@ -56,7 +56,7 @@ public class RoyalMail4State extends Symbol { column = 5; } - dest.append(RoyalTable[(6 * row) + column]); + dest.append(royalTable[(6 * row) + column]); encodeInfo.append("Check Digit: ").append((6 * row) + column).append("\n"); @@ -75,7 +75,7 @@ public class RoyalMail4State extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock; int x, y, w, h; getRectangles().clear(); @@ -109,4 +109,17 @@ public class RoyalMail4State extends Symbol { symbolWidth = pattern[0].length() * 3; symbolHeight = 8; } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.ROYALMAIL4STATE; + } + + @Override + public RoyalMail4State provide() { + return new RoyalMail4State(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Symbol.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Symbol.java old mode 100755 new mode 100644 index 77b321b..b51b529 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Symbol.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Symbol.java @@ -1,1058 +1,63 @@ package org.xbib.graphics.barcode; -import static org.xbib.graphics.barcode.HumanReadableLocation.BOTTOM; -import static org.xbib.graphics.barcode.HumanReadableLocation.NONE; -import static org.xbib.graphics.barcode.HumanReadableLocation.TOP; import org.xbib.graphics.barcode.util.Hexagon; import org.xbib.graphics.barcode.util.TextBox; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; import java.util.List; -/** - * Generic barcode symbology class. - * TODO: Setting attributes like module width, font size, etc should probably throw - * an exception if set *after* encoding has already been completed. - */ -public abstract class Symbol { +public interface Symbol { - private final List rectangles = new ArrayList<>(); + void setDataType(SymbolDataType symbolDataType); - private final List texts = new ArrayList<>(); + void setBarHeight(int barHeight); - private final List hexagons = new ArrayList<>(); + int getBarHeight(); - private final List target = new ArrayList<>(); + void setModuleWidth(int moduleWidth); - protected String content; + int getModuleWidth(); - protected StringBuilder readable; + void setFontName(String fontName); - protected String[] pattern; + String getFontName(); - protected int rowCount = 0; + void setFontSize(double fontSize); - protected int[] rowHeight; + double getFontSize(); - protected StringBuilder errorMsg = new StringBuilder(); + void setContent(String inputData); - protected int symbolHeight = 0; + String getContent(); - protected int symbolWidth = 0; + void setHumanReadableLocation(HumanReadableLocation humanReadableLocation); - protected int defaultHeight = 40; + HumanReadableLocation getHumanReadableLocation(); - private HumanReadableLocation humanReadableLocation = BOTTOM; + int getHumanReadableHeight(); - protected StringBuilder encodeInfo = new StringBuilder(); + void setQuietZoneVertical(int quietZoneVertical); - protected byte[] inputBytes; + int getQuietZoneVertical(); - protected DataType inputDataType = DataType.ECI; + void setQuietZoneHorizontal(int quietZoneHorizontal); - int moduleWidth = 1; + int getQuietZoneHorizontal(); - double fontSize = 8; + int getHeight(); - boolean readerInit; + int getWidth(); - int eciMode = 3; + boolean encode(); - private int quietZoneHorizontal = 0; + void plotSymbol(); - private int quietZoneVertical = 0; + List getRectangles(); - private String fontName = "Helvetica"; + List getTexts(); - public Symbol() { - unsetReaderInit(); - } + List getHexagons(); - public List getRectangles() { - return rectangles; - } + List getTarget(); - public List getTexts() { - return texts; - } - - public List getHexagons() { - return hexagons; - } - - public List getTarget() { - return target; - } - - /** - * Inserts the specified array into the specified original array at the specified index. - * - * @param original the original array into which we want to insert another array - * @param index the index at which we want to insert the array - * @param inserted the array that we want to insert - * @return the combined array - */ - static int[] insert(int[] original, int index, int[] inserted) { - int[] modified = new int[original.length + inserted.length]; - System.arraycopy(original, 0, modified, 0, index); - System.arraycopy(inserted, 0, modified, index, inserted.length); - System.arraycopy(original, index, modified, index + inserted.length, modified.length - index - inserted.length); - return modified; - } - - /** - * Returns true if the specified array contains the specified value. - * - * @param values the array to check in - * @param value the value to check for - * @return true if the specified array contains the specified value - */ - static boolean contains(int[] values, int value) { - for (int value1 : values) { - if (value1 == value) { - return true; - } - } - return false; - } - - private static boolean roughlyEqual(double d1, double d2) { - return Math.abs(d1 - d2) < 0.0001; - } - - /** - * Sets the type of input data. This setting influences what - * pre-processing is done on data before encoding in the symbol. - * For example: for GS1 mode the AI data will be used to - * calculate the position of 'FNC1' characters. - * Valid values are: - *
    - *
  • UTF8 (default) Unicode encoding - *
  • LATIN1 ISO 8859-1 (Latin-1) encoding - *
  • BINARY Byte encoding mode - *
  • GS1 Application Identifier and data pairs in "[AI]DATA" format - *
  • HIBC Health Industry Bar Code number (without check digit) - *
  • ECI Extended Channel Interpretations - *
- * - * @param dataType A DataType value which specifies the type of data - */ - public void setDataType(DataType dataType) { - inputDataType = dataType; - } - - /** - * Prefixes symbol data with a "Reader Initialisation" or "Reader - * Programming" instruction. - */ - public final void setReaderInit() { - readerInit = true; - } - - /** - * Removes "Reader Initialisation" or "Reader Programming" instruction - * from symbol data. - */ - private void unsetReaderInit() { - readerInit = false; - } - - /** - * Returns the default bar height for this symbol. - * - * @return the default bar height for this symbol - */ - public int getBarHeight() { - return defaultHeight; - } - - /** - * Sets the default bar height for this symbol (default value is 40). - * - * @param barHeight the default bar height for this symbol - */ - public void setBarHeight(int barHeight) { - this.defaultHeight = barHeight; - } - - /** - * Returns the module width for this symbol. - * - * @return the module width for this symbol - */ - public int getModuleWidth() { - return moduleWidth; - } - - /** - * Sets the module width for this symbol (default value is 1). - * - * @param moduleWidth the module width for this symbol - */ - public void setModuleWidth(int moduleWidth) { - this.moduleWidth = moduleWidth; - } - - /** - * Returns the horizontal quiet zone (white space) added to the left and to the right of this symbol. - * - * @return the horizontal quiet zone (white space) added to the left and to the right of this symbol - */ - public int getQuietZoneHorizontal() { - return quietZoneHorizontal; - } - - /** - * Sets the horizontal quiet zone (white space) added to the left and to the right of this symbol. - * - * @param quietZoneHorizontal the horizontal quiet zone (white space) added to the left and to the right of this symbol - */ - public void setQuietZoneHorizontal(int quietZoneHorizontal) { - this.quietZoneHorizontal = quietZoneHorizontal; - } - - /** - * Returns the vertical quiet zone (white space) added above and below this symbol. - * - * @return the vertical quiet zone (white space) added above and below this symbol - */ - public int getQuietZoneVertical() { - return quietZoneVertical; - } - - /** - * Sets the vertical quiet zone (white space) added above and below this symbol. - * - * @param quietZoneVertical the vertical quiet zone (white space) added above and below this symbol - */ - public void setQuietZoneVertical(int quietZoneVertical) { - this.quietZoneVertical = quietZoneVertical; - } - - /** - * Returns the name of the font to use to render the human-readable text. - * - * @return the name of the font to use to render the human-readable text - */ - public String getFontName() { - return fontName; - } - - /** - * Sets the name of the font to use to render the human-readable text (default value is Helvetica). - * - * @param fontName the name of the font to use to render the human-readable text - */ - public void setFontName(String fontName) { - this.fontName = fontName; - } - - /** - * Returns the size of the font to use to render the human-readable text. - * - * @return the size of the font to use to render the human-readable text - */ - public double getFontSize() { - return fontSize; - } - - /** - * Sets the size of the font to use to render the human-readable text (default value is 8). - * - * @param fontSize the size of the font to use to render the human-readable text - */ - public void setFontSize(double fontSize) { - this.fontSize = fontSize; - } - - /** - * Gets the width of the encoded symbol, including the horizontal quiet zone. - * - * @return the width of the encoded symbol - */ - public int getWidth() { - return symbolWidth + (2 * quietZoneHorizontal); - } - - /** - * Returns the height of the symbol, including the human-readable text, if any, as well as the vertical - * quiet zone. This height is an approximation, since it is calculated without access to a font engine. - * - * @return the height of the symbol, including the human-readable text, if any, as well as the vertical - * quiet zone - */ - public int getHeight() { - return symbolHeight + getHumanReadableHeight() + (2 * quietZoneVertical); - } - - /** - * Returns the height of the human-readable text, including the space between the text and other symbols. - * This height is an approximation, since it is calculated without access to a font engine. - * - * @return the height of the human-readable text - */ - public int getHumanReadableHeight() { - if (texts.isEmpty()) { - return 0; - } else { - return getTheoreticalHumanReadableHeight(); - } - } - - /** - * Returns the height of the human-readable text, assuming this symbol had human-readable text. - * - * @return the height of the human-readable text, assuming this symbol had human-readable text - */ - int getTheoreticalHumanReadableHeight() { - return (int) Math.ceil(fontSize * 1.2); // 0.2 space between bars and text - } - - /** - * Returns a human readable summary of the decisions made by the encoder when creating a symbol. - * - * @return a human readable summary of the decisions made by the encoder when creating a symbol - */ - public String getEncodeInfo() { - return encodeInfo.toString(); - } - - /** - * Returns the location of the human-readable text. - * - * @return the location of the human-readable text - */ - public HumanReadableLocation getHumanReadableLocation() { - return humanReadableLocation; - } - - /** - * Sets the location of the human-readable text (default value is {@link HumanReadableLocation#BOTTOM}). - * - * @param humanReadableLocation the location of the human-readable text - */ - public void setHumanReadableLocation(HumanReadableLocation humanReadableLocation) { - this.humanReadableLocation = humanReadableLocation; - } - - protected int positionOf(char thischar, char[] LookUp) { - int i, outval = 0; - - for (i = 0; i < LookUp.length; i++) { - if (thischar == LookUp[i]) { - outval = i; - } - } - return outval; - } - - protected String bin2pat(String bin) { - boolean black; - int i, l; - StringBuilder pat = new StringBuilder(); - - black = true; - l = 0; - for (i = 0; i < bin.length(); i++) { - if (black) { - if (bin.charAt(i) == '1') { - l++; - } else { - black = false; - pat.append((char) (l + '0')); - l = 1; - } - } else { - if (bin.charAt(i) == '0') { - l++; - } else { - black = true; - pat.append((char) (l + '0')); - l = 1; - } - } - } - pat.append((char) (l + '0')); - - return pat.toString(); - } - - public String getContent() { - return content; - } - - /** - * Set the data to be encoded. Input data will be assumed to be of - * the type set by setDataType. - * - * @param inputData A String containing the data to encode - */ - public void setContent(String inputData) { - int i; - content = inputData; - if (inputDataType == DataType.GS1) { - content = gs1SanityCheck(inputData); - } - if (inputDataType == DataType.GS1) { - readable = new StringBuilder(); - for (i = 0; i < inputData.length(); i++) { - switch (inputData.charAt(i)) { - case '[': - readable.append('('); - break; - case ']': - readable.append(')'); - break; - default: - readable.append(inputData.charAt(i)); - break; - } - } - } - if (inputDataType == DataType.HIBC) { - content = hibcProcess(inputData); - } - if (!content.isEmpty()) { - if (!encode()) { - throw new IllegalStateException(errorMsg.toString()); - } - } else { - throw new IllegalStateException("No input data"); - } - } - - void eciProcess() { - int qmarksBefore, qmarksAfter; - int i; - - qmarksBefore = 0; - for (i = 0; i < content.length(); i++) { - if (content.charAt(i) == '?') { - qmarksBefore++; - } - } - - qmarksAfter = eciEncode("ISO8859_1"); - if (qmarksAfter == qmarksBefore) { - eciMode = 3; - encodeInfo.append("Encoding in ISO 8859-1 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_2"); - if (qmarksAfter == qmarksBefore) { - eciMode = 4; - encodeInfo.append("Encoding in ISO 8859-2 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_3"); - if (qmarksAfter == qmarksBefore) { - eciMode = 5; - encodeInfo.append("Encoding in ISO 8859-3 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_4"); - if (qmarksAfter == qmarksBefore) { - eciMode = 6; - encodeInfo.append("Encoding in ISO 8859-4 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_5"); - if (qmarksAfter == qmarksBefore) { - eciMode = 7; - encodeInfo.append("Encoding in ISO 8859-5 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_6"); - if (qmarksAfter == qmarksBefore) { - eciMode = 8; - encodeInfo.append("Encoding in ISO 8859-6 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_7"); - if (qmarksAfter == qmarksBefore) { - eciMode = 9; - encodeInfo.append("Encoding in ISO 8859-7 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_8"); - if (qmarksAfter == qmarksBefore) { - eciMode = 10; - encodeInfo.append("Encoding in ISO 8859-8 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_9"); - if (qmarksAfter == qmarksBefore) { - eciMode = 11; - encodeInfo.append("Encoding in ISO 8859-9 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_10"); - if (qmarksAfter == qmarksBefore) { - eciMode = 12; - encodeInfo.append("Encoding in ISO 8859-10 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_11"); - if (qmarksAfter == qmarksBefore) { - eciMode = 13; - encodeInfo.append("Encoding in ISO 8859-11 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_13"); - if (qmarksAfter == qmarksBefore) { - eciMode = 15; - encodeInfo.append("Encoding in ISO 8859-13 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_14"); - if (qmarksAfter == qmarksBefore) { - eciMode = 16; - encodeInfo.append("Encoding in ISO 8859-14 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_15"); - if (qmarksAfter == qmarksBefore) { - eciMode = 17; - encodeInfo.append("Encoding in ISO 8859-15 character set\n"); - return; - } - - qmarksAfter = eciEncode("ISO8859_16"); - if (qmarksAfter == qmarksBefore) { - eciMode = 18; - encodeInfo.append("Encoding in ISO 8859-16 character set\n"); - return; - } - - qmarksAfter = eciEncode("Windows_1250"); - if (qmarksAfter == qmarksBefore) { - eciMode = 21; - encodeInfo.append("Encoding in Windows-1250 character set\n"); - return; - } - - qmarksAfter = eciEncode("Windows_1251"); - if (qmarksAfter == qmarksBefore) { - eciMode = 22; - encodeInfo.append("Encoding in Windows-1251 character set\n"); - return; - } - - qmarksAfter = eciEncode("Windows_1252"); - if (qmarksAfter == qmarksBefore) { - eciMode = 23; - encodeInfo.append("Encoding in Windows-1252 character set\n"); - return; - } - - qmarksAfter = eciEncode("Windows_1256"); - if (qmarksAfter == qmarksBefore) { - eciMode = 24; - encodeInfo.append("Encoding in Windows-1256 character set\n"); - return; - } - - qmarksAfter = eciEncode("SJIS"); - if (qmarksAfter == qmarksBefore) { - eciMode = 20; - encodeInfo.append("Encoding in Shift-JIS character set\n"); - return; - } - - /* default */ - qmarksAfter = eciEncode("UTF8"); - eciMode = 26; - encodeInfo.append("Encoding in UTF-8 character set\n"); - } - - private int eciEncode(String charset) { - /* getBytes replaces unconverted characters to '?', so count - the number of question marks to find if conversion was sucessful. - */ - int i, qmarksAfter; - - try { - inputBytes = content.getBytes(charset); - } catch (UnsupportedEncodingException e) { - return -1; - } - - qmarksAfter = 0; - for (i = 0; i < inputBytes.length; i++) { - if (inputBytes[i] == '?') { - qmarksAfter++; - } - } - - return qmarksAfter; - } - - abstract boolean encode(); - - protected void plotSymbol() { - int xBlock, yBlock; - double x, y, w, h; - boolean black; - rectangles.clear(); - texts.clear(); - int baseY; - if (humanReadableLocation == TOP) { - baseY = getTheoreticalHumanReadableHeight(); - } else { - baseY = 0; - } - h = 0; - y = baseY; - for (yBlock = 0; yBlock < rowCount; yBlock++) { - black = true; - x = 0; - for (xBlock = 0; xBlock < pattern[yBlock].length(); xBlock++) { - char c = pattern[yBlock].charAt(xBlock); - w = getModuleWidth(c - '0') * moduleWidth; - if (black) { - if (rowHeight[yBlock] == -1) { - h = defaultHeight; - } else { - h = rowHeight[yBlock]; - } - if (w != 0 && h != 0) { - Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); - rectangles.add(rect); - } - if (x + w > symbolWidth) { - symbolWidth = (int) Math.ceil(x + w); - } - } - black = !black; - x += w; - } - if ((y - baseY + h) > symbolHeight) { - symbolHeight = (int) Math.ceil(y - baseY + h); - } - y += h; - } - mergeVerticalBlocks(); - if (humanReadableLocation != NONE && readable.length() > 0) { - double baseline; - if (humanReadableLocation == TOP) { - baseline = fontSize; - } else { - baseline = getHeight() + fontSize; - } - double centerX = getWidth() / 2.0; - texts.add(new TextBox(centerX, baseline, readable.toString())); - } - } - - /** - * Returns the module width to use for the specified original module width, taking into account any module width ratio - * customizations. Intended to be overridden by subclasses that support such module width ratio customization. - * - * @param originalWidth the original module width - * @return the module width to use for the specified original module width - */ - protected double getModuleWidth(int originalWidth) { - return originalWidth; - } - - /** - * Search for rectangles which have the same width and x position, and - * which join together vertically and merge them together to reduce the - * number of rectangles needed to describe a symbol. - */ - void mergeVerticalBlocks() { - for (int i = 0; i < rectangles.size() - 1; i++) { - for (int j = i + 1; j < rectangles.size(); j++) { - Rectangle2D.Double firstRect = rectangles.get(i); - Rectangle2D.Double secondRect = rectangles.get(j); - if (roughlyEqual(firstRect.x, secondRect.x) && roughlyEqual(firstRect.width, secondRect.width)) { - if (roughlyEqual(firstRect.y + firstRect.height, secondRect.y)) { - firstRect.height += secondRect.height; - rectangles.set(i, firstRect); - rectangles.remove(j); - } - } - } - } - } - - private String gs1SanityCheck(String source) { - // Enforce compliance with GS1 General Specification - // http://www.gs1.org/docs/gsmp/barcodes/GS1_General_Specifications.pdf - - StringBuilder reduced = new StringBuilder(); - - int i, j, lastAi; - boolean aiLatch; - int bracketLevel, maxBracketLevel, aiLength, maxAiLength, minAiLength; - int[] aiValue = new int[100]; - int[] aiLocation = new int[100]; - int aiCount; - int[] dataLocation = new int[100]; - int[] dataLength = new int[100]; - int srcLen = source.length(); - int errorLatch; - - /* Detect extended ASCII characters */ - for (i = 0; i < srcLen; i++) { - if (source.charAt(i) >= 128) { - errorMsg.append("Extended ASCII characters are not supported by GS1"); - return ""; - } - if (source.charAt(i) < 32) { - errorMsg.append("Control characters are not supported by GS1"); - return ""; - } - } - - if (source.charAt(0) != '[') { - errorMsg.append("Data does not start with an AI"); - return ""; - } - - /* Check the position of the brackets */ - bracketLevel = 0; - maxBracketLevel = 0; - aiLength = 0; - maxAiLength = 0; - minAiLength = 5; - j = 0; - aiLatch = false; - for (i = 0; i < srcLen; i++) { - aiLength += j; - if (((j == 1) && (source.charAt(i) != ']')) - && ((source.charAt(i) < '0') || (source.charAt(i) > '9'))) { - aiLatch = true; - } - if (source.charAt(i) == '[') { - bracketLevel++; - j = 1; - } - if (source.charAt(i) == ']') { - bracketLevel--; - if (aiLength < minAiLength) { - minAiLength = aiLength; - } - j = 0; - aiLength = 0; - } - if (bracketLevel > maxBracketLevel) { - maxBracketLevel = bracketLevel; - } - if (aiLength > maxAiLength) { - maxAiLength = aiLength; - } - } - minAiLength--; - - if (bracketLevel != 0) { - /* Not all brackets are closed */ - errorMsg.append("Malformed AI in input data (brackets don't match)"); - return ""; - } - - if (maxBracketLevel > 1) { - /* Nested brackets */ - errorMsg.append("Found nested brackets in input data"); - return ""; - } - - if (maxAiLength > 4) { - /* AI is too long */ - errorMsg.append("Invalid AI in input data (AI too long)"); - return ""; - } - - if (minAiLength <= 1) { - /* AI is too short */ - errorMsg.append("Invalid AI in input data (AI too short)"); - return ""; - } - - if (aiLatch) { - /* Non-numeric data in AI */ - errorMsg.append("Invalid AI in input data (non-numeric characters in AI)"); - return ""; - } - - aiCount = 0; - for (i = 1; i < srcLen; i++) { - if (source.charAt(i - 1) == '[') { - aiLocation[aiCount] = i; - aiValue[aiCount] = 0; - for (j = 0; source.charAt(i + j) != ']'; j++) { - aiValue[aiCount] *= 10; - aiValue[aiCount] += Character.getNumericValue(source.charAt(i + j)); - } - aiCount++; - } - } - - for (i = 0; i < aiCount; i++) { - dataLocation[i] = aiLocation[i] + 3; - if (aiValue[i] >= 100) { - dataLocation[i]++; - } - if (aiValue[i] >= 1000) { - dataLocation[i]++; - } - dataLength[i] = source.length() - dataLocation[i]; - for (j = source.length() - 1; j >= dataLocation[i]; j--) { - if (source.charAt(j) == '[') { - dataLength[i] = j - dataLocation[i]; - } - } - } - - for (i = 0; i < aiCount; i++) { - if (dataLength[i] == 0) { - /* No data for given AI */ - errorMsg.append("Empty data field in input data"); - return ""; - } - } - - errorLatch = 0; - for (i = 0; i < aiCount; i++) { - switch (aiValue[i]) { - case 0: - if (dataLength[i] != 18) { - errorLatch = 1; - } - break; - case 1: - case 2: - case 3: - if (dataLength[i] != 14) { - errorLatch = 1; - } - break; - case 4: - if (dataLength[i] != 16) { - errorLatch = 1; - } - break; - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - case 18: - case 19: - if (dataLength[i] != 6) { - errorLatch = 1; - } - break; - case 20: - if (dataLength[i] != 2) { - errorLatch = 1; - } - break; - case 23: - case 24: - case 25: - case 39: - case 40: - case 41: - case 42: - case 70: - case 80: - case 81: - errorLatch = 2; - break; - } - if ( - ((aiValue[i] >= 100) && (aiValue[i] <= 179)) - || ((aiValue[i] >= 1000) && (aiValue[i] <= 1799)) - || ((aiValue[i] >= 200) && (aiValue[i] <= 229)) - || ((aiValue[i] >= 2000) && (aiValue[i] <= 2299)) - || ((aiValue[i] >= 300) && (aiValue[i] <= 309)) - || ((aiValue[i] >= 3000) && (aiValue[i] <= 3099)) - || ((aiValue[i] >= 31) && (aiValue[i] <= 36)) - || ((aiValue[i] >= 310) && (aiValue[i] <= 369))) { - errorLatch = 2; - } - if ((aiValue[i] >= 3100) && (aiValue[i] <= 3699) && dataLength[i] != 6) { - errorLatch = 1; - } - if ( - ((aiValue[i] >= 370) && (aiValue[i] <= 379)) - || ((aiValue[i] >= 3700) && (aiValue[i] <= 3799))) { - errorLatch = 2; - } - if ((aiValue[i] >= 410) && (aiValue[i] <= 415)) { - if (dataLength[i] != 13) { - errorLatch = 1; - } - } - if ( - ((aiValue[i] >= 4100) && (aiValue[i] <= 4199)) - || ((aiValue[i] >= 700) && (aiValue[i] <= 703)) - || ((aiValue[i] >= 800) && (aiValue[i] <= 810)) - || ((aiValue[i] >= 900) && (aiValue[i] <= 999)) - || ((aiValue[i] >= 9000) && (aiValue[i] <= 9999))) { - errorLatch = 2; - } - - if (errorLatch == 1) { - errorMsg = new StringBuilder("Invalid data length for AI"); - return ""; - } - - if (errorLatch == 2) { - errorMsg = new StringBuilder("Invalid AI value"); - return ""; - } - } - - /* Resolve AI data - put resulting string in 'reduced' */ - aiLatch = false; - for (i = 0; i < srcLen; i++) { - if ((source.charAt(i) != '[') && (source.charAt(i) != ']')) { - reduced.append(source.charAt(i)); - } - if (source.charAt(i) == '[') { - /* Start of an AI string */ - if (aiLatch) { - reduced.append('['); - } - lastAi = (10 * Character.getNumericValue(source.charAt(i + 1))) - + Character.getNumericValue(source.charAt(i + 2)); - aiLatch = ((lastAi < 0) || (lastAi > 4)) - && ((lastAi < 11) || (lastAi > 20)) - && (lastAi != 23) /* legacy support - see 5.3.8.2.2 */ - && ((lastAi < 31) || (lastAi > 36)) - && (lastAi != 41); - } - /* The ']' character is simply dropped from the input */ - } - - /* the character '[' in the reduced string refers to the FNC1 character */ - return reduced.toString(); - } - - private String hibcProcess(String source) { - char[] hibcCharTable = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', - 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', - 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', - '/', '+', '%'}; - - int counter, i; - String toProcess; - char checkDigit; - - if (source.length() > 36) { - errorMsg = new StringBuilder("Data too long for HIBC LIC"); - return ""; - } - source = source.toUpperCase(); - if (!(source.matches("[A-Z0-9-\\. \\$/+\\%]+?"))) { - errorMsg = new StringBuilder("Invalid characters in input"); - return ""; - } - - counter = 41; - for (i = 0; i < source.length(); i++) { - counter += positionOf(source.charAt(i), hibcCharTable); - } - counter = counter % 43; - - if (counter < 10) { - checkDigit = (char) (counter + '0'); - } else { - if (counter < 36) { - checkDigit = (char) ((counter - 10) + 'A'); - } else { - switch (counter) { - case 36: - checkDigit = '-'; - break; - case 37: - checkDigit = '.'; - break; - case 38: - checkDigit = ' '; - break; - case 39: - checkDigit = '$'; - break; - case 40: - checkDigit = '/'; - break; - case 41: - checkDigit = '+'; - break; - case 42: - checkDigit = '%'; - break; - default: - checkDigit = ' '; - break; /* Keep compiler happy */ - } - } - } - - encodeInfo.append("HIBC Check Digit: ").append(counter).append(" (").append(checkDigit).append(")\n"); - - toProcess = "+" + source + checkDigit; - return toProcess; - } - - /** - * Returns the intermediate coding of this bar code. Symbol types that use the test - * infrastructure should override this method. - * - * @return the intermediate coding of this bar code - */ - protected int[] getCodewords() { - throw new UnsupportedOperationException(); - } - - /** - * Returns this bar code's pattern, converted into a set of corresponding codewords. - * Useful for bar codes that encode their content as a pattern. - * - * @param size the number of digits in each codeword - * @return this bar code's pattern, converted into a set of corresponding codewords - */ - int[] getPatternAsCodewords(int size) { - if (pattern == null || pattern.length == 0) { - return new int[0]; - } else { - int count = (int) Math.ceil(pattern[0].length() / (double) size); - int[] codewords = new int[pattern.length * count]; - for (int i = 0; i < pattern.length; i++) { - String row = pattern[i]; - for (int j = 0; j < count; j++) { - int substringStart = j * size; - int substringEnd = Math.min((j + 1) * size, row.length()); - codewords[(i * count) + j] = Integer.parseInt(row.substring(substringStart, substringEnd)); - } - } - return codewords; - } - } - - public enum DataType { - UTF8, LATIN1, BINARY, GS1, HIBC, ECI - } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/SymbolDataType.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/SymbolDataType.java new file mode 100644 index 0000000..5df816f --- /dev/null +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/SymbolDataType.java @@ -0,0 +1,5 @@ +package org.xbib.graphics.barcode; + +public enum SymbolDataType { + UTF8, LATIN1, BINARY, GS1, HIBC, ECI +} diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/SymbolProvider.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/SymbolProvider.java new file mode 100644 index 0000000..1fe311c --- /dev/null +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/SymbolProvider.java @@ -0,0 +1,8 @@ +package org.xbib.graphics.barcode; + +public interface SymbolProvider { + + boolean canProvide(SymbolType type); + + S provide(); +} diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/SymbolType.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/SymbolType.java new file mode 100644 index 0000000..7f0e0d6 --- /dev/null +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/SymbolType.java @@ -0,0 +1,46 @@ +package org.xbib.graphics.barcode; + +public enum SymbolType { + AUSTRALIA_POST, + AZTEC_CODE, + AZTEC_RUNE, + CHANNEL_CODE, + CODABAR, + CODABLOCK_F, + CODE25, + CODE39, + CODE39_EXT, + CODE11, + CODE16K, + CODE32, + CODE49, + CODE93, + CODE128, + CODEONE, + COMPOSITE, + DATABAR_14, + DATABAR_EXPANDED, + DATABAR_LIMITED, + DATA_MATRIX, + EAN, + GRID_MATRIX, + JAPAN_POST, + KIX_CODE, + KOREA_POST, + LOG_MARS, + MAXI_CODE, + MICRO_QRCODE, + MSI_PLESSEY, + NVE_18, + PDF417, + PHARMACODE, + PHARMACODE2TRACK, + PHARMAZENTRALNUMMER, + POSTNET, + QRCODE, + ROYALMAIL4STATE, + TELEPEN, + UPC, + USPS_ONE_CODE, + USPS_PACKAGE +} diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Telepen.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Telepen.java index 39f11a1..4f044f0 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Telepen.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Telepen.java @@ -7,9 +7,9 @@ package org.xbib.graphics.barcode; * or pairs consisting of a numerical digit followed an X character. * Telepen Numeric also includes a modulo-127 check digit. */ -public class Telepen extends Symbol { +public class Telepen extends AbstractSymbol { - public tp_mode mode; + public Mode mode; private String[] TeleTable = { "1111111111111111", "1131313111", "33313111", "1111313131", "3111313111", "11333131", "13133131", "111111313111", "31333111", @@ -42,20 +42,20 @@ public class Telepen extends Symbol { }; public Telepen() { - mode = tp_mode.NORMAL; + mode = Mode.NORMAL; } public void setNormalMode() { - mode = tp_mode.NORMAL; + mode = Mode.NORMAL; } public void setNumericMode() { - mode = tp_mode.NUMERIC; + mode = Mode.NUMERIC; } @Override public boolean encode() { - if (mode == tp_mode.NORMAL) { + if (mode == Mode.NORMAL) { return normal_mode(); } else { return numeric_mode(); @@ -171,7 +171,20 @@ public class Telepen extends Symbol { return true; } - public enum tp_mode { + public enum Mode { NORMAL, NUMERIC } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.TELEPEN; + } + + @Override + public Telepen provide() { + return new Telepen(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Upc.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Upc.java index 43ca5dc..5e5c9c6 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Upc.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/Upc.java @@ -18,7 +18,7 @@ import java.awt.geom.Rectangle2D; * * @author Robert Elliott */ -public class Upc extends Symbol { +public class Upc extends AbstractSymbol { private boolean useAddOn; @@ -330,7 +330,7 @@ public class Upc extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock; int x, y, w, h; boolean black; @@ -442,4 +442,17 @@ public class Upc extends Symbol { public enum Mode { UPCA, UPCE } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.UPC; + } + + @Override + public Upc provide() { + return new Upc(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/UspsOneCode.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/UspsOneCode.java index e3461b8..f63c316 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/UspsOneCode.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/UspsOneCode.java @@ -15,13 +15,13 @@ import java.math.BigInteger; * tracking code, followed by a dash (-), followed by a delivery point * zip-code which can be 0, 5, 9 or 11 digits in length. */ -public class UspsOneCode extends Symbol { +public class UspsOneCode extends AbstractSymbol { /* The following lookup tables were generated using the code in Appendix C */ - private int[] byte_array = new int[13]; + private final int[] byte_array = new int[13]; - private int[] AppxD_I = { /* Appendix D Table 1 - 5 of 13 characters */ + private final int[] appxD_I = { /* Appendix D Table 1 - 5 of 13 characters */ 0x001F, 0x1F00, 0x002F, 0x1E80, 0x0037, 0x1D80, 0x003B, 0x1B80, 0x003D, 0x1780, 0x003E, 0x0F80, 0x004F, 0x1E40, 0x0057, 0x1D40, 0x005B, 0x1B40, 0x005D, 0x1740, 0x005E, 0x0F40, 0x0067, 0x1CC0, 0x006B, 0x1AC0, 0x006D, 0x16C0, 0x006E, 0x0EC0, @@ -310,7 +310,7 @@ public class UspsOneCode extends Symbol { for (i = 0; i < 10; i++) { if (codeword[i] < 1287) { - characters[i] = AppxD_I[codeword[i]]; + characters[i] = appxD_I[codeword[i]]; } else { characters[i] = AppxD_II[codeword[i] - 1287]; } @@ -393,7 +393,7 @@ public class UspsOneCode extends Symbol { } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock, shortHeight, longHeight; double x, y, w, h; getRectangles().clear(); @@ -448,4 +448,17 @@ public class UspsOneCode extends Symbol { getTexts().add(new TextBox(centerX, baseline, readable.toString())); } } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.USPS_ONE_CODE; + } + + @Override + public UspsOneCode provide() { + return new UspsOneCode(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/UspsPackage.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/UspsPackage.java index 0289810..9a4b7a1 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/UspsPackage.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/UspsPackage.java @@ -8,7 +8,7 @@ import java.awt.geom.Rectangle2D; * A linear barcode based on GS1-128. Includes additional data checks. * Specification at https://ribbs.usps.gov/intelligentmail_package/documents/tech_guides/BarcodePackageIMSpec.pdf */ -public class UspsPackage extends Symbol { +public class UspsPackage extends AbstractSymbol { @Override public boolean encode() { @@ -31,7 +31,7 @@ public class UspsPackage extends Symbol { Code128 code128 = new Code128(); code128.unsetCc(); - code128.setDataType(DataType.GS1); + code128.setDataType(SymbolDataType.GS1); code128.setContent(content); if (content.length() > 4) { @@ -66,12 +66,11 @@ public class UspsPackage extends Symbol { rowHeight = new int[1]; rowHeight[0] = -1; plotSymbol(); - return true; } @Override - protected void plotSymbol() { + public void plotSymbol() { int xBlock; int x, y, w, h; boolean black; @@ -111,4 +110,17 @@ public class UspsPackage extends Symbol { getTexts().add(new TextBox(centerX, getHeight() - 6.0, readable.toString())); getTexts().add(new TextBox(centerX, 12.0, banner)); } + + public static class Provider implements SymbolProvider { + + @Override + public boolean canProvide(SymbolType type) { + return type == SymbolType.USPS_PACKAGE; + } + + @Override + public UspsPackage provide() { + return new UspsPackage(); + } + } } diff --git a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/render/GraphicsRenderer.java b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/render/BarcodeGraphicsRenderer.java similarity index 91% rename from graphics-barcode/src/main/java/org/xbib/graphics/barcode/render/GraphicsRenderer.java rename to graphics-barcode/src/main/java/org/xbib/graphics/barcode/render/BarcodeGraphicsRenderer.java index 285cf7b..a9c83e6 100755 --- a/graphics-barcode/src/main/java/org/xbib/graphics/barcode/render/GraphicsRenderer.java +++ b/graphics-barcode/src/main/java/org/xbib/graphics/barcode/render/BarcodeGraphicsRenderer.java @@ -21,7 +21,7 @@ import java.util.Map; /** * Renders symbologies using the Java Graphics API. */ -public class GraphicsRenderer { +public class BarcodeGraphicsRenderer { /** * The graphics to render to. @@ -60,13 +60,13 @@ public class GraphicsRenderer { * @param antialias if true give anti alias hint * @param transparentBackground if true background should be transparent */ - public GraphicsRenderer(Graphics2D g2d, - Rectangle rectangle, - double scalingFactor, - Color background, - Color foreground, - boolean antialias, - boolean transparentBackground) { + public BarcodeGraphicsRenderer(Graphics2D g2d, + Rectangle rectangle, + double scalingFactor, + Color background, + Color foreground, + boolean antialias, + boolean transparentBackground) { this.g2d = g2d; this.rectangle = rectangle; this.scalingFactor = scalingFactor; @@ -90,7 +90,9 @@ public class GraphicsRenderer { g2d.setBackground(background); if (!transparentBackground) { g2d.setColor(background); - g2d.fill(rectangle); + if (rectangle != null) { + g2d.fill(rectangle); + } g2d.setColor(foreground); } for (Rectangle2D.Double rect : symbol.getRectangles()) { diff --git a/graphics-barcode/src/main/resources/META-INF/services/org.xbib.graphics.barcode.SymbolProvider b/graphics-barcode/src/main/resources/META-INF/services/org.xbib.graphics.barcode.SymbolProvider new file mode 100644 index 0000000..98d4ecb --- /dev/null +++ b/graphics-barcode/src/main/resources/META-INF/services/org.xbib.graphics.barcode.SymbolProvider @@ -0,0 +1,42 @@ +org.xbib.graphics.barcode.AustraliaPost$Provider +org.xbib.graphics.barcode.AztecCode$Provider +org.xbib.graphics.barcode.AztecRune$Provider +org.xbib.graphics.barcode.ChannelCode$Provider +org.xbib.graphics.barcode.Codabar$Provider +org.xbib.graphics.barcode.CodablockF$Provider +org.xbib.graphics.barcode.Code2Of5$Provider +org.xbib.graphics.barcode.Code3Of9$Provider +org.xbib.graphics.barcode.Code3Of9Extended$Provider +org.xbib.graphics.barcode.Code11$Provider +org.xbib.graphics.barcode.Code16k$Provider +org.xbib.graphics.barcode.Code32$Provider +org.xbib.graphics.barcode.Code49$Provider +org.xbib.graphics.barcode.Code93$Provider +org.xbib.graphics.barcode.Code128$Provider +org.xbib.graphics.barcode.CodeOne$Provider +org.xbib.graphics.barcode.Composite$Provider +org.xbib.graphics.barcode.DataBar14$Provider +org.xbib.graphics.barcode.DataBarExpanded$Provider +org.xbib.graphics.barcode.DataBarLimited$Provider +org.xbib.graphics.barcode.DataMatrix$Provider +org.xbib.graphics.barcode.Ean$Provider +org.xbib.graphics.barcode.GridMatrix$Provider +org.xbib.graphics.barcode.JapanPost$Provider +org.xbib.graphics.barcode.KixCode$Provider +org.xbib.graphics.barcode.KoreaPost$Provider +org.xbib.graphics.barcode.Logmars$Provider +org.xbib.graphics.barcode.MaxiCode$Provider +org.xbib.graphics.barcode.MicroQrCode$Provider +org.xbib.graphics.barcode.MsiPlessey$Provider +org.xbib.graphics.barcode.Nve18$Provider +org.xbib.graphics.barcode.Pdf417$Provider +org.xbib.graphics.barcode.Pharmacode$Provider +org.xbib.graphics.barcode.Pharmacode2Track$Provider +org.xbib.graphics.barcode.Pharmazentralnummer$Provider +org.xbib.graphics.barcode.Postnet$Provider +org.xbib.graphics.barcode.QrCode$Provider +org.xbib.graphics.barcode.RoyalMail4State$Provider +org.xbib.graphics.barcode.Telepen$Provider +org.xbib.graphics.barcode.Upc$Provider +org.xbib.graphics.barcode.UspsOneCode$Provider +org.xbib.graphics.barcode.UspsPackage$Provider diff --git a/graphics-barcode/src/test/java/org/xbib/graphics/barcode/SymbolTest.java b/graphics-barcode/src/test/java/org/xbib/graphics/barcode/SymbolTest.java index d4aefbb..a370d84 100755 --- a/graphics-barcode/src/test/java/org/xbib/graphics/barcode/SymbolTest.java +++ b/graphics-barcode/src/test/java/org/xbib/graphics/barcode/SymbolTest.java @@ -24,7 +24,7 @@ import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.runners.Parameterized; import org.reflections.Reflections; -import org.xbib.graphics.barcode.render.GraphicsRenderer; +import org.xbib.graphics.barcode.render.BarcodeGraphicsRenderer; import java.awt.Color; import java.awt.Font; import java.awt.FontFormatException; @@ -87,7 +87,7 @@ public class SymbolTest { private static Font DEJA_VU_SANS; /** The type of symbology being tested. */ - private final Class< ? extends Symbol> symbolType; + private final Class< ? extends AbstractSymbol> symbolType; /** The test configuration properties. */ private final Map< String, String > properties; @@ -121,9 +121,9 @@ public class SymbolTest { } String backend = "org.xbib.graphics.barcode"; Reflections reflections = new Reflections(backend); - Set< Class< ? extends Symbol >> symbols = reflections.getSubTypesOf(Symbol.class); + Set< Class< ? extends AbstractSymbol>> symbols = reflections.getSubTypesOf(AbstractSymbol.class); List< Object[] > data = new ArrayList<>(); - for (Class< ? extends Symbol > symbol : symbols) { + for (Class< ? extends AbstractSymbol> symbol : symbols) { String symbolName = symbol.getSimpleName().toLowerCase(); String dir = "src/test/resources/" + backend.replace('.', '/') + "/" + symbolName; for (File file : getPropertiesFiles(dir)) { @@ -148,7 +148,7 @@ public class SymbolTest { * @param pngFile the file containing the expected final rendering of the bar code, if this test verifies successful behavior * @param errorFile the file containing the expected error message, if this test verifies a failure */ - public SymbolTest(Class< ? extends Symbol > symbolType, + public SymbolTest(Class< ? extends AbstractSymbol> symbolType, Map< String, String > properties, File codewordsFile, File pngFile, @@ -167,7 +167,7 @@ public class SymbolTest { */ @TestTemplate public void test() throws Exception { - Symbol symbol = symbolType.getDeclaredConstructor().newInstance(); + AbstractSymbol symbol = symbolType.getDeclaredConstructor().newInstance(); symbol.setFontName(DEJA_VU_SANS.getFontName()); try { setProperties(symbol, properties); @@ -189,7 +189,7 @@ public class SymbolTest { * @throws IOException if there is any I/O error * @throws ReaderException if ZXing has an issue decoding the barcode image */ - private void verifySuccess(Symbol symbol) throws IOException, ReaderException { + private void verifySuccess(AbstractSymbol symbol) throws IOException, ReaderException { if (symbol.errorMsg.length() > 0) { fail("got error message: " + symbol.errorMsg); } @@ -242,7 +242,7 @@ public class SymbolTest { * @param symbol the symbol to be read * @return a ZXing reader that can read the specified symbol */ - private static Reader findReader(Symbol symbol) { + private static Reader findReader(AbstractSymbol symbol) { if (symbol instanceof Code93) { return new Code93Reader(); } else if (symbol instanceof Code3Of9) { @@ -282,7 +282,7 @@ public class SymbolTest { * @param symbol the symbol which encoded the content * @return the barcode content, without the checksum */ - private static String removeChecksum(String s, Symbol symbol) { + private static String removeChecksum(String s, AbstractSymbol symbol) { if (symbol instanceof Ean || symbol instanceof Upc) { return s.substring(0, s.length() - 1); } else { @@ -298,7 +298,7 @@ public class SymbolTest { * @param symbol the symbol which encoded the content * @return the barcode content, without the start/stop characters */ - private static String removeStartStopChars(String s, Symbol symbol) { + private static String removeStartStopChars(String s, AbstractSymbol symbol) { if (symbol instanceof Codabar) { return s.substring(1, s.length() - 1); } else { @@ -312,7 +312,7 @@ public class SymbolTest { * @param symbol the symbol to check * @throws IOException if there is any I/O error */ - private void verifyError(Symbol symbol) throws IOException { + private void verifyError(AbstractSymbol symbol) throws IOException { String expectedError = Files.readAllLines(errorFile.toPath(), StandardCharsets.UTF_8).get(0); assertTrue(symbol.errorMsg.toString().contains(expectedError)); } @@ -323,7 +323,7 @@ public class SymbolTest { * @param symbol the symbol to generate expectation files for * @throws IOException if there is any I/O error */ - private void generateExpectationFiles(Symbol symbol) throws IOException { + private void generateExpectationFiles(AbstractSymbol symbol) throws IOException { if (symbol.errorMsg != null && symbol.errorMsg.length() > 0) { generateErrorExpectationFile(symbol); } else { @@ -338,7 +338,7 @@ public class SymbolTest { * @param symbol the symbol to generate the error expectation file for * @throws IOException if there is any I/O error */ - private void generateErrorExpectationFile(Symbol symbol) throws IOException { + private void generateErrorExpectationFile(AbstractSymbol symbol) throws IOException { if (!errorFile.exists()) { PrintWriter writer = new PrintWriter(errorFile); writer.println(symbol.errorMsg); @@ -352,7 +352,7 @@ public class SymbolTest { * @param symbol the symbol to generate codewords for * @throws IOException if there is any I/O error */ - private void generateCodewordsExpectationFile(Symbol symbol) throws IOException { + private void generateCodewordsExpectationFile(AbstractSymbol symbol) throws IOException { //if (!codewordsFile.exists()) { PrintWriter writer = new PrintWriter(codewordsFile); try { @@ -375,7 +375,7 @@ public class SymbolTest { * @param symbol the symbol to draw * @throws IOException if there is any I/O error */ - private void generatePngExpectationFile(Symbol symbol) throws IOException { + private void generatePngExpectationFile(AbstractSymbol symbol) throws IOException { BufferedImage img = draw(symbol, 10.0d); if (img != null) { ImageIO.write(img, "png", pngFile); @@ -403,14 +403,14 @@ public class SymbolTest { * @param symbol the symbol to draw * @return the resultant image */ - private static BufferedImage draw(Symbol symbol, double scalingFactor) { + private static BufferedImage draw(AbstractSymbol symbol, double scalingFactor) { int width = (int) (symbol.getWidth() * scalingFactor); int height = (int) (symbol.getHeight() * scalingFactor); if (width > 0 && height > 0) { BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); Rectangle rectangle = new Rectangle(0, 0, img.getWidth(), img.getHeight()); Graphics2D g2d = img.createGraphics(); - GraphicsRenderer renderer = new GraphicsRenderer(g2d, rectangle, + BarcodeGraphicsRenderer renderer = new BarcodeGraphicsRenderer(g2d, rectangle, scalingFactor, Color.WHITE, Color.BLACK, true, false); renderer.render(symbol); g2d.dispose(); @@ -427,7 +427,7 @@ public class SymbolTest { * @param properties the attribute names and values to set * @throws ReflectiveOperationException if there is any reflection error */ - private static void setProperties(Symbol symbol, Map< String, String > properties) throws ReflectiveOperationException { + private static void setProperties(AbstractSymbol symbol, Map< String, String > properties) throws ReflectiveOperationException { for (Map.Entry< String, String > entry : properties.entrySet()) { String name = entry.getKey(); String value = entry.getValue(); diff --git a/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/Code39Test.java b/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/Code39Test.java index 19859d9..744a035 100644 --- a/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/Code39Test.java +++ b/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/Code39Test.java @@ -3,7 +3,7 @@ package org.xbib.graphics.barcode.output; import org.junit.jupiter.api.Test; import org.xbib.graphics.barcode.Code3Of9; import org.xbib.graphics.barcode.HumanReadableLocation; -import org.xbib.graphics.barcode.render.GraphicsRenderer; +import org.xbib.graphics.barcode.render.BarcodeGraphicsRenderer; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Rectangle; @@ -26,7 +26,7 @@ public class Code39Test { int width = (int) (code3Of9.getWidth() * scalingFactor); int height = (int) (code3Of9.getHeight() * scalingFactor); BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); - GraphicsRenderer renderer = createRenderer(bufferedImage, scalingFactor); + BarcodeGraphicsRenderer renderer = createRenderer(bufferedImage, scalingFactor); renderer.render(code3Of9); renderer.close(); OutputStream outputStream = Files.newOutputStream(Paths.get("build/barcode1.png")); @@ -42,7 +42,7 @@ public class Code39Test { //code3Of9.setContent("20180123456"); code3Of9.setContent("11111111111"); BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); - GraphicsRenderer renderer = createRenderer(bufferedImage, 3.0d); + BarcodeGraphicsRenderer renderer = createRenderer(bufferedImage, 3.0d); renderer.render(code3Of9); renderer.close(); OutputStream outputStream = Files.newOutputStream(Paths.get("build/barcode2.png")); @@ -50,9 +50,9 @@ public class Code39Test { outputStream.close(); } - private GraphicsRenderer createRenderer(BufferedImage bufferedImage, double scalingFactor) { + private BarcodeGraphicsRenderer createRenderer(BufferedImage bufferedImage, double scalingFactor) { Graphics2D g2d = bufferedImage.createGraphics(); Rectangle rectangle = new Rectangle(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()); - return new GraphicsRenderer(g2d, rectangle, scalingFactor, Color.WHITE, Color.BLACK, false, false); + return new BarcodeGraphicsRenderer(g2d, rectangle, scalingFactor, Color.WHITE, Color.BLACK, false, false); } } diff --git a/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/EPSRendererTest.java b/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/EPSRendererTest.java index 215422e..9085eb3 100755 --- a/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/EPSRendererTest.java +++ b/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/EPSRendererTest.java @@ -4,10 +4,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; import org.xbib.graphics.barcode.Code93; -import org.xbib.graphics.barcode.render.GraphicsRenderer; +import org.xbib.graphics.barcode.render.BarcodeGraphicsRenderer; import org.xbib.graphics.barcode.MaxiCode; -import org.xbib.graphics.barcode.Symbol; +import org.xbib.graphics.barcode.AbstractSymbol; import org.xbib.graphics.io.vector.eps.EPSGraphics2D; import java.awt.Color; import java.awt.Font; @@ -22,6 +24,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.Locale; +@DisabledOnOs(OS.MAC) public class EPSRendererTest { private Locale originalDefaultLocale; @@ -83,7 +86,7 @@ public class EPSRendererTest { test(maxicode, 5, Color.WHITE, Color.BLACK, 5, "maxicode-basic.eps"); } - private void test(Symbol symbol, + private void test(AbstractSymbol symbol, double magnification, Color paper, Color ink, @@ -96,10 +99,10 @@ public class EPSRendererTest { Rectangle rectangle = new Rectangle(0, 0, width, height); EPSGraphics2D epsGraphics2D = new EPSGraphics2D(rectangle); epsGraphics2D.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12)); - GraphicsRenderer graphicsRenderer = new GraphicsRenderer(epsGraphics2D, rectangle, + BarcodeGraphicsRenderer barcodeGraphicsRenderer = new BarcodeGraphicsRenderer(epsGraphics2D, rectangle, magnification, paper, ink, false, false); - graphicsRenderer.render(symbol); - graphicsRenderer.close(); + barcodeGraphicsRenderer.render(symbol); + barcodeGraphicsRenderer.close(); byte[] actualBytes = epsGraphics2D.getBytes(); String actual = new String(actualBytes, StandardCharsets.UTF_8); try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get("build/" + expectationFile))) { diff --git a/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/PDFRendererTest.java b/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/PDFRendererTest.java index 2696540..a7f726d 100755 --- a/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/PDFRendererTest.java +++ b/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/PDFRendererTest.java @@ -4,10 +4,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; import org.xbib.graphics.barcode.Code93; import org.xbib.graphics.barcode.MaxiCode; -import org.xbib.graphics.barcode.Symbol; -import org.xbib.graphics.barcode.render.GraphicsRenderer; +import org.xbib.graphics.barcode.AbstractSymbol; +import org.xbib.graphics.barcode.render.BarcodeGraphicsRenderer; import org.xbib.graphics.io.vector.pdf.PDFGraphics2D; import java.awt.Color; import java.awt.Font; @@ -22,6 +24,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.Locale; +@DisabledOnOs(OS.MAC) public class PDFRendererTest { private Locale originalDefaultLocale; @@ -83,7 +86,7 @@ public class PDFRendererTest { test(maxicode, 5, Color.WHITE, Color.BLACK, 5, "maxicode-basic.pdf"); } - private void test(Symbol symbol, + private void test(AbstractSymbol symbol, double magnification, Color paper, Color ink, @@ -96,10 +99,10 @@ public class PDFRendererTest { Rectangle rectangle = new Rectangle(0, 0, width, height); PDFGraphics2D pdfGraphics2D = new PDFGraphics2D(rectangle); pdfGraphics2D.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12)); - GraphicsRenderer graphicsRenderer = new GraphicsRenderer(pdfGraphics2D, + BarcodeGraphicsRenderer barcodeGraphicsRenderer = new BarcodeGraphicsRenderer(pdfGraphics2D, rectangle, magnification, paper, ink, false, false); - graphicsRenderer.render(symbol); - graphicsRenderer.close(); + barcodeGraphicsRenderer.render(symbol); + barcodeGraphicsRenderer.close(); byte[] actualBytes = pdfGraphics2D.getBytes(); String actual = new String(actualBytes, StandardCharsets.UTF_8); try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get("build/" + expectationFile))) { diff --git a/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/SvgRendererTest.java b/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/SvgRendererTest.java index 1f7c27f..18baca6 100755 --- a/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/SvgRendererTest.java +++ b/graphics-barcode/src/test/java/org/xbib/graphics/barcode/output/SvgRendererTest.java @@ -4,10 +4,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; import org.xbib.graphics.barcode.Code93; import org.xbib.graphics.barcode.MaxiCode; -import org.xbib.graphics.barcode.Symbol; -import org.xbib.graphics.barcode.render.GraphicsRenderer; +import org.xbib.graphics.barcode.AbstractSymbol; +import org.xbib.graphics.barcode.render.BarcodeGraphicsRenderer; import org.xbib.graphics.io.vector.svg.SVGGraphics2D; import java.awt.Color; import java.awt.Font; @@ -22,6 +24,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.Locale; +@DisabledOnOs(OS.MAC) public class SvgRendererTest { private Locale originalDefaultLocale; @@ -83,7 +86,7 @@ public class SvgRendererTest { test(maxicode, 1.0, Color.WHITE, Color.BLACK, 5, "maxicode-basic.svg"); } - private void test(Symbol symbol, + private void test(AbstractSymbol symbol, double magnification, Color paper, Color ink, @@ -96,10 +99,10 @@ public class SvgRendererTest { Rectangle rectangle = new Rectangle(0, 0, width, height); SVGGraphics2D svgGraphics2D = new SVGGraphics2D(rectangle); svgGraphics2D.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12)); - GraphicsRenderer graphicsRenderer = new GraphicsRenderer(svgGraphics2D, + BarcodeGraphicsRenderer barcodeGraphicsRenderer = new BarcodeGraphicsRenderer(svgGraphics2D, rectangle, magnification, paper, ink, false, false); - graphicsRenderer.render(symbol); - graphicsRenderer.close(); + barcodeGraphicsRenderer.render(symbol); + barcodeGraphicsRenderer.close(); byte[] actualBytes = svgGraphics2D.getBytes(); String actual = new String(actualBytes, StandardCharsets.UTF_8); try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get("build/" + expectationFile))) { diff --git a/graphics-ghostscript/src/main/java/module-info.java b/graphics-ghostscript/src/main/java/module-info.java index fded08f..ad1693b 100644 --- a/graphics-ghostscript/src/main/java/module-info.java +++ b/graphics-ghostscript/src/main/java/module-info.java @@ -1,5 +1,6 @@ module org.xbib.graphics.ghostscript { exports org.xbib.graphics.ghostscript; + requires com.sun.jna; requires java.logging; requires transitive java.desktop; requires transitive org.apache.pdfbox; diff --git a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/Ghostscript.java b/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/Ghostscript.java index 3ce99fb..2792f50 100644 --- a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/Ghostscript.java +++ b/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/Ghostscript.java @@ -8,7 +8,6 @@ import org.xbib.graphics.ghostscript.internal.GhostscriptLibraryLoader; import org.xbib.graphics.ghostscript.internal.LoggingOutputStream; import org.xbib.graphics.ghostscript.internal.NullOutputStream; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -32,7 +31,7 @@ public class Ghostscript { private static final Logger logger = Logger.getLogger(Ghostscript.class.getName()); - public static final String ENCODING_PARAMETER = "org.xbib.graphics.gs.encoding"; + public static final String ENCODING_PARAMETER = "org.xbib.graphics.ghostscript.encoding"; private static Ghostscript instance; @@ -46,26 +45,21 @@ public class Ghostscript { private static OutputStream stdErr; - private static String tmpDir; + private static Path tmpDir; private Ghostscript() { } public static synchronized Ghostscript getInstance() throws IOException { if (instance == null) { - tmpDir = System.getenv("TMPDIR"); - if (tmpDir != null) { - if (new File(tmpDir).mkdirs()) { - logger.info("tmp dir " + tmpDir + " created"); - } - } + prepareTmp(); instance = new Ghostscript(); libraryInstance = getGhostscriptLibrary(); nativeInstanceByRef = getNativeInstanceByRef(); - //stdOut = new LoggingOutputStream(logger); - //stdErr = new LoggingOutputStream(logger); - //instance.setStdOut(stdOut); - //instance.setStdErr(stdErr); + stdOut = new LoggingOutputStream(logger); + stdErr = new LoggingOutputStream(logger); + instance.setStdOut(stdOut); + instance.setStdErr(stdErr); } return instance; } @@ -96,19 +90,16 @@ public class Ghostscript { * @throws IOException if delete of instance fails */ public static synchronized void deleteInstance() throws IOException { - if (libraryInstance != null) { - //libraryInstance.gsapi_exit(nativeInstanceByRef.getValue()); - libraryInstance.gsapi_delete_instance(nativeInstanceByRef.getValue()); - libraryInstance = null; - } - if (nativeInstanceByRef != null) { - nativeInstanceByRef = null; - } if (instance != null) { - if (tmpDir != null) { - deleteGs(tmpDir); + if (libraryInstance != null) { + libraryInstance.gsapi_delete_instance(nativeInstanceByRef.getValue()); + libraryInstance = null; + } + if (nativeInstanceByRef != null) { + nativeInstanceByRef = null; } instance = null; + deleteTmp(); } } @@ -118,6 +109,7 @@ public class Ghostscript { * @return the Ghostscript revision data. */ public static GhostscriptRevision getRevision() { + getGhostscriptLibrary(); GhostscriptLibrary.gsapi_revision_s revision = new GhostscriptLibrary.gsapi_revision_s(); libraryInstance.gsapi_revision(revision, revision.size()); GhostscriptRevision result = new GhostscriptRevision(); @@ -192,9 +184,8 @@ public class Ghostscript { * @throws IOException if initialize fails */ public void initialize(String[] args) throws IOException { - if (libraryInstance == null || nativeInstanceByRef == null) { - return; - } + getGhostscriptLibrary(); + getNativeInstanceByRef(); int result; GhostscriptLibrary.stdin_fn stdinCallback = null; if (getStdIn() != null) { @@ -265,12 +256,10 @@ public class Ghostscript { * @throws IOException if exit fails */ public void exit() throws IOException { - if (libraryInstance == null || nativeInstanceByRef == null) { - return; - } + getGhostscriptLibrary(); + getNativeInstanceByRef(); Pointer pointer = nativeInstanceByRef.getValue(); if (pointer != null) { - logger.info("gsapi_exit " + pointer); int result = libraryInstance.gsapi_exit(pointer); if (result != 0) { throw new IOException("can not exit Ghostscript interpreter, error code " + result); @@ -286,9 +275,8 @@ public class Ghostscript { * @throws IOException if run fails */ public void runString(String string) throws IOException { - if (libraryInstance == null || nativeInstanceByRef == null) { - return; - } + getGhostscriptLibrary(); + getNativeInstanceByRef(); IntByReference exitCode = new IntByReference(); libraryInstance.gsapi_run_string_begin(nativeInstanceByRef.getValue(), 0, exitCode); if (exitCode.getValue() != 0) { @@ -320,9 +308,8 @@ public class Ghostscript { * @throws IOException if run of file fails */ public void runFile(String fileName) throws IOException { - if (libraryInstance == null || nativeInstanceByRef == null) { - return; - } + getGhostscriptLibrary(); + getNativeInstanceByRef(); IntByReference exitCode = new IntByReference(); libraryInstance.gsapi_run_file(nativeInstanceByRef.getValue(), fileName, 0, exitCode); if (exitCode.getValue() != 0) { @@ -330,25 +317,47 @@ public class Ghostscript { } } - private static void deleteGs(String path) throws IOException { - logger.info("delete gs_* in " + path); + private static void prepareTmp() throws IOException { + String tmp = System.getenv("TMPDIR"); // the variable that ghostscript uses + if (tmp == null) { + tmp = System.getenv("TEMP"); + } + if (tmp == null) { + throw new IllegalStateException("no TEMP/TMPDIR environment set for ghostscript"); + } + tmpDir = Paths.get(tmp); + deleteTmp(); + Files.createDirectories(tmpDir); + } + + private static void deleteTmp() throws IOException { + if (tmpDir == null) { + return; + } + if (!Files.exists(tmpDir)) { + return; + } try { - Files.walkFileTree(Paths.get(path), new SimpleFileVisitor<>() { + Files.walkFileTree(tmpDir, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (file.startsWith("gs_")) { - Files.delete(file); - } + logger.info("deleting " + file); + Files.deleteIfExists(file); return FileVisitResult.CONTINUE; } @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) { + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + logger.info("deleting " + dir); + Files.deleteIfExists(dir); return FileVisitResult.CONTINUE; } }); } catch (NoSuchFileException e) { logger.log(Level.WARNING, e.getMessage(), e); + } finally { + logger.info("deleting " + tmpDir); + Files.deleteIfExists(tmpDir); } } } diff --git a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PDFConverter.java b/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PDFConverter.java index 9d83324..a369e96 100644 --- a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PDFConverter.java +++ b/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PDFConverter.java @@ -5,11 +5,15 @@ import org.xbib.graphics.ghostscript.internal.LoggingOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.LinkedList; import java.util.List; -import java.util.ResourceBundle; +import java.util.logging.Level; import java.util.logging.Logger; public class PDFConverter { @@ -68,6 +72,8 @@ public class PDFConverter { */ private final PaperSize paperSize; + private final Path tmpPath; + public PDFConverter() { this(OPTION_AUTOROTATEPAGES_OFF, OPTION_PROCESSCOLORMODEL_RGB, OPTION_PDFSETTINGS_PRINTER, "1.4", false, PaperSize.A4); @@ -80,10 +86,11 @@ public class PDFConverter { boolean pdfx, PaperSize paperSize) { this.autoRotatePages = autoRotatePages; this.processColorModel = processColorModel; - this. pdfsettings = pdfsettings; + this.pdfsettings = pdfsettings; this.compatibilityLevel = compatibilityLevel; this.pdfx = pdfx; this.paperSize = paperSize; + this.tmpPath = Paths.get("/var/tmp/" + this); } /** @@ -98,6 +105,8 @@ public class PDFConverter { if (outputStream == null) { return; } + prepare(tmpPath); + Path output = Files.createTempFile(tmpPath, "pdf", "pdf"); Ghostscript gs = Ghostscript.getInstance(); List gsArgs = new LinkedList<>(); gsArgs.add("-ps2pdf"); @@ -151,7 +160,6 @@ public class PDFConverter { gsArgs.add("-dDEVICEWIDTHPOINTS=" + paperSize.getWidth()); gsArgs.add("-dDEVICEHEIGHTPOINTS=" + paperSize.getHeight()); gsArgs.add("-sDEVICE=pdfwrite"); - Path output = Files.createTempFile("pdf", "pdf"); gsArgs.add("-sOutputFile=" + output.toAbsolutePath().toString()); //gsArgs.add("-q"); gsArgs.add("-f"); @@ -164,7 +172,39 @@ public class PDFConverter { Files.copy(output.toAbsolutePath(), outputStream); } finally { Ghostscript.deleteInstance(); - Files.delete(output); + delete(tmpPath); + } + } + + private static void prepare(Path path) throws IOException { + Files.createDirectories(path); + } + + private static void delete(Path path) throws IOException { + if (path == null) { + return; + } + if (!Files.exists(path)) { + return; + } + try { + Files.walkFileTree(path.toAbsolutePath(), new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.deleteIfExists(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.deleteIfExists(dir); + return FileVisitResult.CONTINUE; + } + }); + } catch (Exception e) { + logger.log(Level.WARNING, e.getMessage(), e); + } finally { + Files.deleteIfExists(path); } } } diff --git a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PDFRasterizer.java b/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PDFRasterizer.java index 1768c6d..b7ba360 100644 --- a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PDFRasterizer.java +++ b/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PDFRasterizer.java @@ -24,6 +24,7 @@ import java.io.OutputStream; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; @@ -52,8 +53,10 @@ public class PDFRasterizer implements Closeable { private final Map imageReaders; + private final Path tmpPath; + public PDFRasterizer(String subject) { - this("org.xbib.graphics.gs/1.0.8", "Jörg Prante ", subject); + this("org.xbib.graphics.ghostscript/4.0.1", "Jörg Prante", subject); } public PDFRasterizer(String creator, String author, String subject) { @@ -61,6 +64,7 @@ public class PDFRasterizer implements Closeable { this.author = author; this.subject = subject; this.imageReaders = createImageReaders(); + this.tmpPath = Paths.get("/var/tmp/" + this); } @Override @@ -79,7 +83,8 @@ public class PDFRasterizer implements Closeable { if (Files.size(source) == 0) { throw new IOException("empty file at " + source.toString()); } - Path tmp = Files.createTempDirectory("pdf-rasterize"); + prepare(tmpPath); + Path tmp = Files.createTempDirectory(tmpPath, "pdf-rasterize"); if (!Files.isWritable(tmp)) { throw new IOException("unable to write to " + tmp.toString()); } @@ -90,7 +95,7 @@ public class PDFRasterizer implements Closeable { scalePDF(tmpTarget, target); logger.info("convert source=" + source + " done"); } finally { - delete(tmp); + delete(tmpPath); } } @@ -102,16 +107,17 @@ public class PDFRasterizer implements Closeable { if (!Files.isReadable(source.toAbsolutePath())) { throw new IOException("unable to read " + source.toString()); } - Path tmp = Files.createTempDirectory("pdf-rasterize"); + prepare(tmpPath); + Path tmp = Files.createTempDirectory(tmpPath, "pdf-rasterize"); try { pdfToGrayScreenImage(source.toAbsolutePath(), tmp); Path tmpTarget = tmp.resolve(target.getFileName()); mergeImagesToPDF(tmp, tmpTarget); //toPDFA(tmpTarget, target); scalePDF(tmpTarget, target); - } finally { - delete(tmp); logger.info("convert source=" + source.toAbsolutePath() + " done"); + } finally { + delete(tmpPath); } } @@ -123,8 +129,8 @@ public class PDFRasterizer implements Closeable { if (!Files.isReadable(source.toAbsolutePath())) { throw new IOException("unable to read " + source.toString()); } + Ghostscript gs = Ghostscript.getInstance(); try { - Ghostscript gs = Ghostscript.getInstance(); List gsArgs = new LinkedList<>(); gsArgs.add("-dNOPAUSE"); gsArgs.add("-dBATCH"); @@ -144,9 +150,9 @@ public class PDFRasterizer implements Closeable { gs.setStdErr(new LoggingOutputStream(logger)); gs.initialize(gsArgs.toArray(new String[gsArgs.size()])); gs.exit(); + logger.info("pdfToImage done"); } finally { Ghostscript.deleteInstance(); - logger.info("pdfToImage done"); } } @@ -155,8 +161,8 @@ public class PDFRasterizer implements Closeable { String prefix, String pageRange) throws IOException { logger.info("pdfToImage source=" + sourceFile + " target=" + targetDir + " started"); + Ghostscript gs = Ghostscript.getInstance(); try { - Ghostscript gs = Ghostscript.getInstance(); List gsArgs = new LinkedList<>(); gsArgs.add("-dNOPAUSE"); gsArgs.add("-dBATCH"); @@ -186,9 +192,9 @@ public class PDFRasterizer implements Closeable { gs.setStdErr(new LoggingOutputStream(logger)); gs.initialize(gsArgs.toArray(new String[gsArgs.size()])); gs.exit(); + logger.info("pdfToImage done"); } finally { Ghostscript.deleteInstance(); - logger.info("pdfToImage done"); } } @@ -266,8 +272,8 @@ public class PDFRasterizer implements Closeable { public synchronized void scalePDF(Path sourceFile, Path targetFile) throws IOException { logger.info("scalePDF: source = " + sourceFile + " target = " + targetFile + " starting"); + Ghostscript gs = Ghostscript.getInstance(); try { - Ghostscript gs = Ghostscript.getInstance(); List gsArgs = new LinkedList<>(); gsArgs.add("-dNOPAUSE"); gsArgs.add("-dBATCH"); @@ -285,18 +291,19 @@ public class PDFRasterizer implements Closeable { logger.info(gsArgs.toString()); gs.initialize(gsArgs.toArray(new String[gsArgs.size()])); gs.exit(); + logger.info("scalePDF: source = " + sourceFile + " target = " + targetFile + " done"); } finally { Ghostscript.deleteInstance(); - logger.info("scalePDF: source = " + sourceFile + " target = " + targetFile + " done"); } } public void toPDFA(Path source, Path target) throws IOException { - Path iccPath = Files.createTempFile("srgb", ".icc"); - Path pdfapsPathTmp = Files.createTempFile("PDFA_def", ".tmp"); - Path pdfapsPath = Files.createTempFile("PDFA_def", ".ps"); + prepare(tmpPath); + Path iccPath = Files.createTempFile(tmpPath, "srgb", ".icc"); + Path pdfapsPathTmp = Files.createTempFile(tmpPath, "PDFA_def", ".tmp"); + Path pdfapsPath = Files.createTempFile(tmpPath, "PDFA_def", ".ps"); + Ghostscript gs = Ghostscript.getInstance(); try { - Ghostscript gs = Ghostscript.getInstance(); Files.copy(getClass().getResourceAsStream("/iccprofiles/srgb.icc"), iccPath, StandardCopyOption.REPLACE_EXISTING); Files.copy(getClass().getResourceAsStream("/lib/PDFA_def.ps"), @@ -434,6 +441,10 @@ public class PDFRasterizer implements Closeable { return pos >= 0 ? filename.substring(pos + 1).toLowerCase(Locale.ROOT) : null; } + private static void prepare(Path path) throws IOException { + Files.createDirectories(path); + } + private static void delete(Path path) throws IOException { if (path == null) { return; @@ -441,7 +452,6 @@ public class PDFRasterizer implements Closeable { if (!Files.exists(path)) { return; } - logger.info("delete " + path); try { Files.walkFileTree(path.toAbsolutePath(), new SimpleFileVisitor<>() { @Override diff --git a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/internal/GhostscriptLibraryLoader.java b/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/internal/GhostscriptLibraryLoader.java index 0d0ea2a..d4c1d8c 100644 --- a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/internal/GhostscriptLibraryLoader.java +++ b/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/internal/GhostscriptLibraryLoader.java @@ -1,5 +1,7 @@ package org.xbib.graphics.ghostscript.internal; +import static com.sun.jna.Platform.LINUX; +import static com.sun.jna.Platform.MAC; import com.sun.jna.Function; import com.sun.jna.Library; import com.sun.jna.Native; @@ -14,20 +16,40 @@ public class GhostscriptLibraryLoader { private static final Logger logger = Logger.getLogger(GhostscriptLibraryLoader.class.getName()); - private static final String[] LIBNAMES = { - "libgs.so.9.25", // RHEL 8 - "libgs.9.25.dylib", // macOS - "gs.9.25" // other + private static final String[] LINUX_LIBNAMES = { + "gs.so.9.27", + "gs.9.27", + "gs.so.9.25", + "gs.9.25", + "gs" + }; + + private static final String[] MAC_LIBNAMES = { + "libgs.9.25.dylib", + "libgs.9.25", + "gs.9.25", + "libgs.dylib", + "libgs", + "gs", }; public static GhostscriptLibrary loadLibrary() { Map options = new HashMap<>(); options.put(Library.OPTION_CALLING_CONVENTION, Function.C_CONVENTION); + String[] LIBNAMES = {}; + switch (Platform.getOSType()) { + case LINUX: + LIBNAMES = LINUX_LIBNAMES; + break; + case MAC: + LIBNAMES = MAC_LIBNAMES; + break; + } for (String libname : LIBNAMES) { try { return Native.load(libname, GhostscriptLibrary.class, options); } catch (Error e) { - logger.log(Level.WARNING, "library " + libname + " not found"); + logger.log(Level.WARNING, "library " + libname + " not found", e); } } return null; diff --git a/graphics-ghostscript/src/test/java/org/xbib/graphics/ghostscript/test/GhostscriptTest.java b/graphics-ghostscript/src/test/java/org/xbib/graphics/ghostscript/test/GhostscriptTest.java index f673d22..0bfdcf1 100644 --- a/graphics-ghostscript/src/test/java/org/xbib/graphics/ghostscript/test/GhostscriptTest.java +++ b/graphics-ghostscript/src/test/java/org/xbib/graphics/ghostscript/test/GhostscriptTest.java @@ -1,5 +1,6 @@ package org.xbib.graphics.ghostscript.test; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -7,13 +8,11 @@ import org.xbib.graphics.ghostscript.Ghostscript; import org.xbib.graphics.ghostscript.GhostscriptRevision; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; public class GhostscriptTest { @@ -27,6 +26,11 @@ public class GhostscriptTest { gs = Ghostscript.getInstance(); } + @AfterAll + public static void down() throws IOException { + Ghostscript.deleteInstance(); + } + @Test public void testGetRevision() { GhostscriptRevision revision = Ghostscript.getRevision(); @@ -76,24 +80,17 @@ public class GhostscriptTest { public void testStdOut() throws IOException { InputStream is = new ByteArrayInputStream("devicenames ==\n".getBytes()); gs.setStdIn(is); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - gs.setStdOut(os); String[] args = { "-dNODISPLAY", "-sOutputFile=%stdout", "-f", "-"}; gs.initialize(args); gs.exit(); - assertTrue(os.toString().length() > 0); - os.close(); is.close(); } @Test public void testStdErr() throws IOException { - ByteArrayOutputStream os = null; try { InputStream is = new ByteArrayInputStream("stupid\n".getBytes()); gs.setStdIn(is); - os = new ByteArrayOutputStream(); - gs.setStdErr(os); String[] args = { "-dNODISPLAY", "-sOutputFile=%stdout", "-f", "-"}; gs.initialize(args); gs.exit(); @@ -102,14 +99,6 @@ public class GhostscriptTest { if (!e.getMessage().contains("error code -100")) { fail(e.getMessage()); } - } finally { - try { - assert os != null; - assertTrue(os.toString().length() > 0); - os.close(); - } catch (IOException e2) { - fail(e2.getMessage()); - } } } } diff --git a/graphics-ghostscript/src/test/resources/logging.properties b/graphics-ghostscript/src/test/resources/logging.properties new file mode 100644 index 0000000..86090ac --- /dev/null +++ b/graphics-ghostscript/src/test/resources/logging.properties @@ -0,0 +1,8 @@ +handlers=java.util.logging.ConsoleHandler,java.util.logging.FileHandler +.level=INFO +java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL %4$-7s [%3$s] %5$s %6$s%n +java.util.logging.ConsoleHandler.level=INFO +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.FileHandler.level=INFO +java.util.logging.FileHandler.pattern=build/test.log +java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter diff --git a/graphics-pdfbox-groovy/LICENSE.txt b/graphics-pdfbox-groovy/LICENSE.txt deleted file mode 100644 index d645695..0000000 --- a/graphics-pdfbox-groovy/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/Barcode.groovy b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/Barcode.groovy index bae6bd1..ee27528 100644 --- a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/Barcode.groovy +++ b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/Barcode.groovy @@ -1,20 +1,22 @@ package org.xbib.graphics.pdfbox.groovy +import org.xbib.graphics.barcode.SymbolType + class Barcode extends BaseNode { Integer x = 0 Integer y = 0 - Integer width = 0 + Integer width - Integer height = 0 + Integer height String value - BarcodeType type + SymbolType symbolType void setType(String type) { - this.type = Enum.valueOf(BarcodeType, type.toUpperCase()) + this.symbolType = Enum.valueOf(SymbolType, type.toUpperCase()) } } diff --git a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/Color.groovy b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/Color.groovy index 0fb00a1..096c6fc 100644 --- a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/Color.groovy +++ b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/Color.groovy @@ -4,8 +4,10 @@ import groovy.transform.AutoClone @AutoClone class Color { + String hex = '000000' - def rgb = [0, 0, 0] + + def rgb = [0f, 0f, 0f] void setColor(String value) { if (value.startsWith('#')) { diff --git a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfDocument.groovy b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfDocument.groovy index e45e233..602e781 100644 --- a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfDocument.groovy +++ b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfDocument.groovy @@ -74,13 +74,8 @@ class PdfDocument implements Closeable { papersize = PDRectangle.A4 break } - switch (document.orientation) { - case 'landscape': + if (document.orientation == 'landscape') { papersize = swapOrientation(papersize) - break; - case 'portrait': - default: - break } document.width = papersize.width as int document.height = papersize.height as int diff --git a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfDocumentBuilder.groovy b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfDocumentBuilder.groovy index 03b348a..d5efa0f 100644 --- a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfDocumentBuilder.groovy +++ b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfDocumentBuilder.groovy @@ -4,9 +4,16 @@ import groovy.transform.InheritConstructors import groovy.util.logging.Log4j2 import groovy.xml.MarkupBuilder import org.apache.pdfbox.pdmodel.common.PDMetadata -import org.xbib.graphics.pdfbox.groovy.* +import org.xbib.graphics.pdfbox.groovy.Barcode +import org.xbib.graphics.pdfbox.groovy.Cell import org.xbib.graphics.pdfbox.groovy.Document +import org.xbib.graphics.pdfbox.groovy.HeaderFooterOptions +import org.xbib.graphics.pdfbox.groovy.Image import org.xbib.graphics.pdfbox.groovy.Line +import org.xbib.graphics.pdfbox.groovy.PageBreak +import org.xbib.graphics.pdfbox.groovy.Row +import org.xbib.graphics.pdfbox.groovy.Table +import org.xbib.graphics.pdfbox.groovy.TextBlock import org.xbib.graphics.pdfbox.groovy.render.ParagraphRenderer import org.xbib.graphics.pdfbox.groovy.render.TableRenderer diff --git a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfboxRenderer.groovy b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfboxRenderer.groovy index 6f2c157..81346b0 100644 --- a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfboxRenderer.groovy +++ b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfboxRenderer.groovy @@ -2,7 +2,7 @@ package org.xbib.graphics.pdfbox.groovy.builder import org.apache.pdfbox.pdmodel.PDPageContentStream import org.xbib.graphics.barcode.HumanReadableLocation -import org.xbib.graphics.barcode.Symbol +import org.xbib.graphics.barcode.AbstractSymbol import org.xbib.graphics.barcode.util.Hexagon import org.xbib.graphics.barcode.util.TextBox @@ -25,7 +25,7 @@ class PdfboxRenderer { this.scalingFactor = scalingfactor } - void render(Symbol symbol) throws IOException { + void render(AbstractSymbol symbol) throws IOException { Integer marginX = (symbol.getQuietZoneHorizontal() * scalingFactor) as Integer Integer marginY = (symbol.getQuietZoneVertical() * scalingFactor) as Integer // rectangles diff --git a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/render/ParagraphRenderer.groovy b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/render/ParagraphRenderer.groovy index abfd40a..996bb97 100644 --- a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/render/ParagraphRenderer.groovy +++ b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/render/ParagraphRenderer.groovy @@ -1,18 +1,17 @@ package org.xbib.graphics.pdfbox.groovy.render import groovy.util.logging.Log4j2 -import org.apache.pdfbox.pdmodel.PDDocument import org.apache.pdfbox.pdmodel.PDPageContentStream -import org.apache.pdfbox.pdmodel.PDResources -import org.apache.pdfbox.pdmodel.common.PDRectangle import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject -import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream -import org.xbib.graphics.barcode.Code3Of9 +import org.apache.pdfbox.util.Matrix import org.xbib.graphics.barcode.HumanReadableLocation -import org.xbib.graphics.barcode.render.GraphicsRenderer +import org.xbib.graphics.barcode.Symbol +import org.xbib.graphics.barcode.SymbolProvider +import org.xbib.graphics.barcode.SymbolType +import org.xbib.graphics.barcode.render.BarcodeGraphicsRenderer import org.xbib.graphics.pdfbox.PdfBoxGraphics2D import org.xbib.graphics.pdfbox.groovy.* import org.xbib.graphics.pdfbox.groovy.builder.PdfDocument @@ -22,7 +21,6 @@ import org.xbib.graphics.pdfbox.groovy.render.element.LineElement import org.xbib.graphics.pdfbox.groovy.render.element.TextElement import javax.imageio.ImageIO -import java.awt.Rectangle import java.awt.image.BufferedImage @Log4j2 @@ -170,15 +168,16 @@ class ParagraphRenderer implements Renderable { } private void renderTextElement(TextElement element) { - Text text = element.node PDPageContentStream contentStream = pdfDocument.contentStream contentStream.beginText() contentStream.newLineAtOffset(pdfDocument.x as float, pdfDocument.translatedY as float) + Text text = element.node def color = text.font.color.rgb contentStream.setNonStrokingColor(color[0], color[1], color[2]) contentStream.setFont(element.pdfFont, text.font.size) + // remove control chars String string = element.text.replaceAll("\\p{C}","") - contentStream.showText(string) // remove control chars + contentStream.showText(string) contentStream.endText() } @@ -199,46 +198,58 @@ class ParagraphRenderer implements Renderable { Image image = element.node ImageType imageType = element.node.type if (imageType) { + PDPageContentStream contentStream = pdfDocument.contentStream InputStream inputStream = new ByteArrayInputStream(element.node.data) // TODO add TwelveMonkeys BufferedImage bufferedImage = ImageIO.read(inputStream) + inputStream.close() BigDecimal x = pdfDocument.x + image.x BigDecimal y = pdfDocument.translateY(pdfDocument.y + image.y) - int width = element.node.width - int height = element.node.height + //int width = image.width + //int height = image.height PDImageXObject img = imageType == ImageType.JPG ? JPEGFactory.createFromImage(pdfDocument.pdDocument, bufferedImage) : LosslessFactory.createFromImage(pdfDocument.pdDocument, bufferedImage) - pdfDocument.contentStream.drawImage(img, x as float, y as float, width, height) - inputStream.close() + Matrix matrix = new Matrix() + matrix.translate(x, y) + contentStream.saveGraphicsState() + contentStream.transform(matrix) + contentStream.drawImage(img, 0f, 0f, image.width, image.height) + contentStream.restoreGraphicsState() } } private void renderBarcodeElement(BarcodeElement element) { - switch (element.node.getType()) { - case BarcodeType.CODE39: - float x = pdfDocument.x + element.node.x - float y = pdfDocument.translateY(pdfDocument.y + element.node.y) - Code3Of9 code3Of9 = new Code3Of9() - code3Of9.setContent(element.node.value) - code3Of9.setBarHeight(element.node.height) - code3Of9.setHumanReadableLocation(HumanReadableLocation.NONE) - PDFormXObject formXObject = createXObject(pdfDocument.pdDocument, x, y) - PdfBoxGraphics2D g2d = new PdfBoxGraphics2D(pdfDocument.pdDocument, formXObject, pdfDocument.contentStream, null) - Rectangle rectangle = new Rectangle(0, 0, x as int, y as int) - GraphicsRenderer graphicsRenderer = - new GraphicsRenderer(g2d, rectangle, 1.0d, java.awt.Color.WHITE, java.awt.Color.BLACK, false, false); - graphicsRenderer.render(code3Of9); - graphicsRenderer.close(); - break + Barcode barcode = element.node + PDPageContentStream contentStream = pdfDocument.contentStream + Symbol symbol = create(barcode.symbolType) + symbol.setContent(barcode.value) + symbol.setBarHeight(barcode.height) + symbol.setHumanReadableLocation(HumanReadableLocation.BOTTOM) + float x = pdfDocument.x + barcode.x + float y = pdfDocument.translateY(pdfDocument.y + barcode.y) + float width = barcode.width + float height = barcode.height + PdfBoxGraphics2D pdfBoxGraphics2D = new PdfBoxGraphics2D(pdfDocument.pdDocument, width, height) + BarcodeGraphicsRenderer renderer = new BarcodeGraphicsRenderer(pdfBoxGraphics2D, null, 1.0d, + java.awt.Color.WHITE, java.awt.Color.BLACK, false, false) + renderer.render(symbol) + renderer.close() + PDFormXObject xFormObject = pdfBoxGraphics2D.getXFormObject() + Matrix matrix = new Matrix() + matrix.translate(x, y) + contentStream.saveGraphicsState() + contentStream.transform(matrix) + contentStream.drawForm(xFormObject) + contentStream.restoreGraphicsState() + } + + private static Symbol create(SymbolType symbolType) { + for (SymbolProvider symbolProvider : ServiceLoader.load(SymbolProvider)) { + if (symbolProvider.canProvide(symbolType)) { + return symbolProvider.provide() + } } + throw new IllegalArgumentException('unknown symbol type ' + symbolType) } - - private static PDFormXObject createXObject(PDDocument document, float x, float y) { - PDFormXObject xFormObject = new PDAppearanceStream(document) - xFormObject.setResources(new PDResources()) - xFormObject.setBBox(new PDRectangle(x, y)) - return xFormObject - } - } diff --git a/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfBoxBarcodeTest.groovy b/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfBoxBarcodeTest.groovy new file mode 100644 index 0000000..12675b6 --- /dev/null +++ b/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfBoxBarcodeTest.groovy @@ -0,0 +1,136 @@ +package org.xbib.graphics.pdfbox.groovy.test + +import org.apache.pdfbox.pdmodel.PDDocument +import org.apache.pdfbox.pdmodel.PDPage +import org.apache.pdfbox.pdmodel.PDPageContentStream +import org.apache.pdfbox.pdmodel.common.PDRectangle +import org.apache.pdfbox.pdmodel.font.PDType1Font +import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject +import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject +import org.apache.pdfbox.util.Matrix +import org.junit.jupiter.api.Test +import org.xbib.graphics.barcode.Code3Of9 +import org.xbib.graphics.barcode.HumanReadableLocation +import org.xbib.graphics.barcode.render.BarcodeGraphicsRenderer +import org.xbib.graphics.pdfbox.PdfBoxGraphics2D + +import java.awt.Color +import java.awt.Graphics2D +import java.awt.Rectangle +import java.awt.image.BufferedImage +import java.nio.file.Files +import java.nio.file.Paths + +class PdfBoxBarcodeTest { + + @Test + void testBarcodeImage() { + PDDocument document = new PDDocument() + PDPage page = new PDPage(PDRectangle.A4) + document.addPage(page) + PDPageContentStream contentStream = new PDPageContentStream(document, page) + BarcodeCreator creator = new ImageBarcodeRenderer(document, contentStream) + createBarcode(page, creator) + createText(page, contentStream) + String filename = "build/pdfbox-with-barcode-image.pdf" + OutputStream outputStream = Files.newOutputStream(Paths.get(filename)) + document.save(outputStream) + } + + @Test + void testBarcodeEmbedded() { + PDDocument document = new PDDocument() + PDPage page = new PDPage(PDRectangle.A4) + document.addPage(page) + PDPageContentStream contentStream = new PDPageContentStream(document, page) + BarcodeCreator creator = new PdfboxBarcodeRenderer(document, contentStream) + createBarcode(page, creator) + createText(page, contentStream) + String filename = "build/pdfbox-with-barcode-embedded.pdf" + OutputStream outputStream = Files.newOutputStream(Paths.get(filename)) + document.save(outputStream) + } + + private void createBarcode(PDPage page, BarcodeCreator barcodeCreator) { + float x = 32.0f + float y = page.getBBox().height - 100.0f + float width = 150f + float height = 75f + Code3Of9 code3Of9 = new Code3Of9() + code3Of9.setContent('1234567890') + code3Of9.setBarHeight(height as int) + code3Of9.setHumanReadableLocation(HumanReadableLocation.BOTTOM) + barcodeCreator.create(x, y, width, height, code3Of9) + } + + private static void createText(PDPage page, PDPageContentStream contentStream) { + contentStream.moveTo(32.0f, (page.getBBox().height - 80f) as float) + contentStream.beginText() + contentStream.setFont(PDType1Font.HELVETICA, 12f) + contentStream.showText("Hello World") + contentStream.endText() + contentStream.close() + } + + interface BarcodeCreator { + void create(float x, float y, float width, float height, Code3Of9 code3Of9) + } + + class ImageBarcodeRenderer implements BarcodeCreator { + + PDDocument document + PDPageContentStream contentStream + + ImageBarcodeRenderer(PDDocument document, PDPageContentStream contentStream) { + this.document = document + this.contentStream = contentStream + } + + @Override + void create(float x, float y, float width, float height, Code3Of9 code3Of9) { + BufferedImage bufferedImage = new BufferedImage(width as int, height as int, BufferedImage.TYPE_BYTE_GRAY) + Graphics2D g2d = bufferedImage.createGraphics() + Rectangle rectangle = new Rectangle(0, 0, width as int, height as int) + BarcodeGraphicsRenderer renderer = new BarcodeGraphicsRenderer(g2d, rectangle, 1.0d, + Color.WHITE, Color.BLACK, false, false) + renderer.render(code3Of9) + renderer.close() + PDImageXObject img = LosslessFactory.createFromImage(document, bufferedImage) + Matrix matrix = new Matrix() + matrix.translate(x, y) + contentStream.saveGraphicsState() + contentStream.transform(matrix) + contentStream.drawImage(img, 0f, 0f, width, height) + contentStream.restoreGraphicsState() + } + } + + class PdfboxBarcodeRenderer implements BarcodeCreator { + + PDDocument document + PDPageContentStream contentStream + + PdfboxBarcodeRenderer(PDDocument document, PDPageContentStream contentStream) { + this.document = document + this.contentStream = contentStream + } + + @Override + void create(float x, float y, float width, float height, Code3Of9 code3Of9) { + PdfBoxGraphics2D pdfBoxGraphics2D = new PdfBoxGraphics2D(document, width, height) + Rectangle rectangle = new Rectangle(0, 0, width as int, height as int) + BarcodeGraphicsRenderer renderer = new BarcodeGraphicsRenderer(pdfBoxGraphics2D, rectangle, 1.0d, + Color.WHITE, Color.BLACK, false, false) + renderer.render(code3Of9) + renderer.close() + PDFormXObject xFormObject = pdfBoxGraphics2D.getXFormObject() + Matrix matrix = new Matrix() + matrix.translate(x, y) + contentStream.saveGraphicsState() + contentStream.transform(matrix) + contentStream.drawForm(xFormObject) + contentStream.restoreGraphicsState() + } + } +} diff --git a/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfDocumentBuilderTest.groovy b/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfDocumentBuilderTest.groovy index fd81196..0570853 100644 --- a/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfDocumentBuilderTest.groovy +++ b/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfDocumentBuilderTest.groovy @@ -2,6 +2,7 @@ package org.xbib.graphics.pdfbox.groovy.test import groovy.util.logging.Log4j2 import org.junit.Test +import org.xbib.graphics.barcode.SymbolType import org.xbib.graphics.pdfbox.groovy.builder.PdfDocumentBuilder import java.nio.file.Files @@ -114,6 +115,36 @@ class PdfDocumentBuilderTest { } } + @Test + void testPdfWithBarcode() { + OutputStream outputStream = Files.newOutputStream(Paths.get('build/barcode.pdf')) + outputStream.withCloseable { + PdfDocumentBuilder builder = new PdfDocumentBuilder(outputStream) + builder.create { + document(font: [family: 'Helvetica'], margin: [top: 1.cm]) { + paragraph(margin: [left: 7.mm, top: 0.cm]) { + text "Hello World 1" + } + paragraph(margin: [left: 7.mm, top: 2.cm]) { + barcode(width: 6.cm, height: 2.cm, value: '20180123456', type: SymbolType.CODE39) + } + paragraph(margin: [left: 7.mm, top: 0.cm]) { + text "Hello World 2" + } + paragraph(margin: [left: 7.mm, top: 1.cm, bottom: 1.cm]) { + barcode(width: 10.cm, height: 0.5.cm, value: 'ABCDEFGHIJKLMN', type: SymbolType.CODE39) + } + paragraph(margin: [left: 7.mm, top: 0.cm]) { + text "Hello World 3" + } + paragraph(margin: [left: 7.mm, top: 4.cm]) { + barcode(width: 6.cm, height: 4.cm, value: 'Hello', type: SymbolType.QRCODE) + } + } + } + } + } + @Test void testPdfWithImage() { byte[] logo = getClass().getResourceAsStream('/img/logo-print.png').bytes @@ -121,7 +152,9 @@ class PdfDocumentBuilderTest { outputStream.withCloseable { PdfDocumentBuilder builder = new PdfDocumentBuilder(outputStream) List layout = [ - [key: 'Typ', value: 'Online', 'bold': true] + [key: 'A', value: '1', 'bold': true], + [key: 'B', value: '2', 'bold': true], + [key: 'C', value: '3', 'bold': true] ] builder.create { document(font: [family: 'Helvetica'], margin: [top: 1.cm]) { @@ -169,23 +202,6 @@ class PdfDocumentBuilderTest { } } - @Test - void testPdfWithBarcode() { - OutputStream outputStream = Files.newOutputStream(Paths.get('build/barcode.pdf')) - outputStream.withCloseable { - PdfDocumentBuilder builder = new PdfDocumentBuilder(outputStream) - builder.create { - document { - paragraph { - text "Hello World" - barcode(x: 10.cm, y: 1.cm, width: 6.cm, height: 2.cm, value: '20180123456') - } - paragraph "Hello World bottom" - } - } - } - } - @Test void testPdfWithCaron() { List fontdefs = [ diff --git a/graphics-pdfbox/NOTICE.txt b/graphics-pdfbox/NOTICE.txt new file mode 100644 index 0000000..142b9d9 --- /dev/null +++ b/graphics-pdfbox/NOTICE.txt @@ -0,0 +1,8 @@ + +This work is based on + +https://github.com/rototor/pdfbox-graphics2d + +as of December 2020 + +APache 2.0 License \ No newline at end of file diff --git a/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/color/RGBtoCMYKColorMapper.java b/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/color/RGBtoCMYKColorMapper.java index 6288ef5..c6749b1 100644 --- a/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/color/RGBtoCMYKColorMapper.java +++ b/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/color/RGBtoCMYKColorMapper.java @@ -5,7 +5,6 @@ import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.graphics.color.PDColor; import org.apache.pdfbox.pdmodel.graphics.color.PDICCBased; -import org.xbib.graphics.pdfbox.color.DefaultColorMapper; import java.awt.Color; import java.awt.color.ICC_ColorSpace; diff --git a/graphics-printer/src/main/java/module-info.java b/graphics-printer/src/main/java/module-info.java new file mode 100644 index 0000000..a726c28 --- /dev/null +++ b/graphics-printer/src/main/java/module-info.java @@ -0,0 +1,5 @@ +module org.xbib.graphics.printer { + exports org.xbib.graphics.printer; + requires java.logging; + requires transitive java.desktop; +} diff --git a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PrintUtility.java b/graphics-printer/src/main/java/org/xbib/graphics/printer/PrintUtility.java similarity index 99% rename from graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PrintUtility.java rename to graphics-printer/src/main/java/org/xbib/graphics/printer/PrintUtility.java index ee4e02b..e17499e 100644 --- a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PrintUtility.java +++ b/graphics-printer/src/main/java/org/xbib/graphics/printer/PrintUtility.java @@ -1,4 +1,4 @@ -package org.xbib.graphics.ghostscript; +package org.xbib.graphics.printer; import javax.print.Doc; import javax.print.DocFlavor; diff --git a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/Printer.java b/graphics-printer/src/main/java/org/xbib/graphics/printer/Printer.java similarity index 99% rename from graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/Printer.java rename to graphics-printer/src/main/java/org/xbib/graphics/printer/Printer.java index 963f8a4..487816e 100644 --- a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/Printer.java +++ b/graphics-printer/src/main/java/org/xbib/graphics/printer/Printer.java @@ -1,4 +1,4 @@ -package org.xbib.graphics.ghostscript; +package org.xbib.graphics.printer; import javax.print.PrintService; import javax.print.attribute.standard.Media; diff --git a/graphics-ghostscript/src/test/java/org/xbib/graphics/ghostscript/test/PrinterTest.java b/graphics-printer/src/test/java/org/xbib/graphics/printer/test/PrinterTest.java similarity index 78% rename from graphics-ghostscript/src/test/java/org/xbib/graphics/ghostscript/test/PrinterTest.java rename to graphics-printer/src/test/java/org/xbib/graphics/printer/test/PrinterTest.java index e0d3fb6..0d72140 100644 --- a/graphics-ghostscript/src/test/java/org/xbib/graphics/ghostscript/test/PrinterTest.java +++ b/graphics-printer/src/test/java/org/xbib/graphics/printer/test/PrinterTest.java @@ -1,7 +1,7 @@ -package org.xbib.graphics.ghostscript.test; +package org.xbib.graphics.printer.test; import org.junit.jupiter.api.Test; -import org.xbib.graphics.ghostscript.PrintUtility; +import org.xbib.graphics.printer.PrintUtility; import java.util.logging.Level; import java.util.logging.Logger; diff --git a/settings.gradle b/settings.gradle index f08e1a8..e7c2511 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,3 +9,4 @@ include 'graphics-ghostscript' include 'graphics-pdfbox' include 'graphics-pdfbox-layout' include 'graphics-pdfbox-groovy' +include 'graphics-printer'