diff --git a/generator/src/main/java/net/codecrete/qrbill/canvas/FontMetrics.java b/generator/src/main/java/net/codecrete/qrbill/canvas/FontMetrics.java index 005f967..c968069 100644 --- a/generator/src/main/java/net/codecrete/qrbill/canvas/FontMetrics.java +++ b/generator/src/main/java/net/codecrete/qrbill/canvas/FontMetrics.java @@ -327,7 +327,7 @@ private char getCharWidth(char ch) { else if (ch >= 0xa0 && ch <= 0x017f) { width = charWidthxA0x17F[ch - 0xa0]; } else if (ch >= 0x0218 && ch <= 0x021b) { - width = charWidthx218x21B[ch - 0xa0]; + width = charWidthx218x21B[ch - 0x0218]; } else if (ch == 0x2013) { width = charNDashWidth; } else if (ch == 0x20AC) { diff --git a/generator/src/main/java/net/codecrete/qrbill/generator/Bill.java b/generator/src/main/java/net/codecrete/qrbill/generator/Bill.java index 2bc1fb5..25daa9d 100644 --- a/generator/src/main/java/net/codecrete/qrbill/generator/Bill.java +++ b/generator/src/main/java/net/codecrete/qrbill/generator/Bill.java @@ -66,6 +66,10 @@ public enum Version { private AlternativeScheme[] alternativeSchemes = null; /** Bill format */ private BillFormat format = new BillFormat(); + /** Data separator for QR code data */ + private QrDataSeparator separator = QrDataSeparator.LF; + /** Character set used for the QR bill data */ + private SPSCharacterSet characterSet = SPSCharacterSet.LATIN_1_SUBSET; /** * Creates a new instance with default values for the format. @@ -240,7 +244,7 @@ public void updateReferenceType() { if (ref != null) { if (ref.startsWith("RF")) referenceType = REFERENCE_TYPE_CRED_REF; - else if (ref.length() > 0) + else if (!ref.isEmpty()) referenceType = REFERENCE_TYPE_QR_REF; else referenceType = REFERENCE_TYPE_NO_REF; @@ -412,6 +416,60 @@ public void setFormat(BillFormat format) { this.format = format; } + /** + * Gets the line separator for the QR code data fields. + *
+ * The default is {@link QrDataSeparator#LF}. There is no need to change it except + * for improving compatibility with a non-compliant software processing the QR code data. + *
+ * @return the line separator for the QR code data fields. + */ + public QrDataSeparator getSeparator() { + return separator; + } + + /** + * Sets the line separator for the QR code data fields. + *+ * The default is {@link QrDataSeparator#LF}. There is no need to change it except + * for improving compatibility with a non-compliant software processing the QR code data. + *
+ * @param separator the line separator for the QR code data fields. + */ + public void setSeparator(QrDataSeparator separator) { + this.separator = separator; + } + + /** + * Gets the character set used for the QR bill data. + *+ * Defaults to {@link SPSCharacterSet#LATIN_1_SUBSET}. + *
+ *+ * Until November 21, 2025, {@link SPSCharacterSet#LATIN_1_SUBSET} is the only value that will generate + * QR bills accepted by all banks. This will change by November 21, 2025. + *
+ * @return the character set used for the QR bill data. + */ + public SPSCharacterSet getCharacterSet() { + return characterSet; + } + + /** + * Sets the character set used for the QR bill data. + *+ * Defaults to {@link SPSCharacterSet#LATIN_1_SUBSET}. + *
+ *+ * Until November 21, 2025, {@link SPSCharacterSet#LATIN_1_SUBSET} is the only value that will generate + * QR bills accepted by all banks. This will change by November 21, 2025. + *
+ * @param characterSet the character set used for the QR bill data. + */ + public void setCharacterSet(SPSCharacterSet characterSet) { + this.characterSet = characterSet; + } + /** * {@inheritDoc} */ @@ -431,7 +489,9 @@ public boolean equals(Object o) { Objects.equals(unstructuredMessage, bill.unstructuredMessage) && Objects.equals(billInformation, bill.billInformation) && Arrays.equals(alternativeSchemes, bill.alternativeSchemes) && - Objects.equals(format, bill.format); + Objects.equals(format, bill.format) && + Objects.equals(separator, bill.separator) && + Objects.equals(characterSet, bill.characterSet); } /** @@ -441,7 +501,7 @@ public boolean equals(Object o) { public int hashCode() { int result = Objects.hash(version, amount, currency, account, creditor, referenceType, reference, - debtor, unstructuredMessage, billInformation, format); + debtor, unstructuredMessage, billInformation, format, separator, characterSet); result = 31 * result + Arrays.hashCode(alternativeSchemes); return result; } @@ -464,6 +524,8 @@ public String toString() { ", billInformation='" + billInformation + '\'' + ", alternativeSchemes=" + Arrays.toString(alternativeSchemes) + ", format=" + format + + ", qrDataSeparator=" + separator + + ", characterSet=" + characterSet + '}'; } } diff --git a/generator/src/main/java/net/codecrete/qrbill/generator/BillFormat.java b/generator/src/main/java/net/codecrete/qrbill/generator/BillFormat.java index f03fc04..5eca176 100644 --- a/generator/src/main/java/net/codecrete/qrbill/generator/BillFormat.java +++ b/generator/src/main/java/net/codecrete/qrbill/generator/BillFormat.java @@ -41,12 +41,6 @@ public class BillFormat implements Serializable { /** ISO country code of local country */ private String localCountryCode = "CH"; - /** Data separator for QR code data */ - private QrDataSeparator qrDataSeparator = QrDataSeparator.LF; - - /** Character set used for the QR bill data */ - private SPSCharacterSet characterSet = SPSCharacterSet.LATIN_1_SUBSET; - /** * Creates a new instance with default values */ @@ -69,8 +63,6 @@ public BillFormat(BillFormat format) { marginLeft = format.marginLeft; marginRight = format.marginRight; localCountryCode = format.localCountryCode; - qrDataSeparator = format.qrDataSeparator; - characterSet = format.characterSet; } /** @@ -324,62 +316,6 @@ public void setLocalCountryCode(String localCountryCode) { this.localCountryCode = localCountryCode; } - /** - * Gets the line separator for the QR code data fields. - *- * The default is {@link QrDataSeparator#LF}. There is no need to change it except - * for improving compatibility with a non-compliant software processing the QR code data. - *
- * @return the line separator for the QR code data fields. - */ - public QrDataSeparator getQrDataSeparator() { - return qrDataSeparator; - } - - /** - * Sets the line separator for the QR code data fields. - *- * The default is {@link QrDataSeparator#LF}. There is no need to change it except - * for improving compatibility with a non-compliant software processing the QR code data. - *
- * @param qrDataSeparator the line separator for the QR code data fields. - */ - public void setQrDataSeparator(QrDataSeparator qrDataSeparator) { - this.qrDataSeparator = qrDataSeparator; - } - - /** - * Gets the character set used for the QR bill data. - *- * Defaults to {@link SPSCharacterSet#LATIN_1_SUBSET}. - *
- *- * Until November 21, 2025, {@link SPSCharacterSet#LATIN_1_SUBSET} is the only value that will generate - * QR bills accepted by all banks. This will change by November 21, 2025. A release after that date - * wil change the default to {@link SPSCharacterSet#EXTENDED_LATIN}. - *
- * @return the character set used for the QR bill data. - */ - public SPSCharacterSet getCharacterSet() { - return characterSet; - } - - /** - * Sets the character set used for the QR bill data. - *- * Defaults to {@link SPSCharacterSet#LATIN_1_SUBSET}. - *
- *- * Until November 21, 2025, {@link SPSCharacterSet#LATIN_1_SUBSET} is the only value that will generate - * QR bills accepted by all banks. This will change by November 21, 2025. A release after that date - * wil change the default to {@link SPSCharacterSet#EXTENDED_LATIN}. - *
- * @param characterSet the character set used for the QR bill data. - */ - public void setCharacterSet(SPSCharacterSet characterSet) { - this.characterSet = characterSet; - } - /** * {@inheritDoc} */ @@ -396,9 +332,7 @@ public boolean equals(Object o) { resolution == that.resolution && marginLeft == that.marginLeft && marginRight == that.marginRight && - Objects.equals(localCountryCode, that.localCountryCode) && - qrDataSeparator == that.qrDataSeparator && - characterSet == that.characterSet; + Objects.equals(localCountryCode, that.localCountryCode); } /** @@ -407,7 +341,7 @@ public boolean equals(Object o) { @Override public int hashCode() { return Objects.hash(outputSize, language, separatorType, fontFamily, graphicsFormat, resolution, marginLeft, - marginLeft, localCountryCode, qrDataSeparator, characterSet); + marginLeft, localCountryCode); } /** @@ -425,8 +359,6 @@ public String toString() { ", marginLeft=" + marginLeft + ", marginRight=" + marginRight + ", localCountryCode='" + localCountryCode + '\'' + - ", qrDataSeparator=" + qrDataSeparator + - ", characterSet=" + characterSet + '}'; } } diff --git a/generator/src/main/java/net/codecrete/qrbill/generator/QRBill.java b/generator/src/main/java/net/codecrete/qrbill/generator/QRBill.java index 82b6b26..f69c807 100644 --- a/generator/src/main/java/net/codecrete/qrbill/generator/QRBill.java +++ b/generator/src/main/java/net/codecrete/qrbill/generator/QRBill.java @@ -134,7 +134,7 @@ public static ValidationResult validate(Bill bill) { * @see #draw */ public static byte[] generate(Bill bill) { - try (Canvas canvas = createCanvas(bill.getFormat())) { + try (Canvas canvas = createCanvas(bill)) { validateAndGenerate(bill, canvas); return ((ByteArrayResult) canvas).toByteArray(); } catch (IOException e) { @@ -277,9 +277,10 @@ public static Bill decodeQrCodeText(String text) { return QRCodeText.decode(text); } - private static Canvas createCanvas(BillFormat format) throws IOException { + private static Canvas createCanvas(Bill bill) throws IOException { double drawingWidth; double drawingHeight; + BillFormat format = bill.getFormat(); // define page size switch (format.getOutputSize()) { @@ -313,7 +314,7 @@ private static Canvas createCanvas(BillFormat format) throws IOException { break; case PDF: canvas = new PDFCanvas(drawingWidth, drawingHeight, - format.getCharacterSet() != SPSCharacterSet.LATIN_1_SUBSET + bill.getCharacterSet() != SPSCharacterSet.LATIN_1_SUBSET ? PDFFontSettings.embeddedLiberationSans() : PDFFontSettings.standardHelvetica()); break; diff --git a/generator/src/main/java/net/codecrete/qrbill/generator/QRCodeText.java b/generator/src/main/java/net/codecrete/qrbill/generator/QRCodeText.java index 28e0479..8a011ae 100644 --- a/generator/src/main/java/net/codecrete/qrbill/generator/QRCodeText.java +++ b/generator/src/main/java/net/codecrete/qrbill/generator/QRCodeText.java @@ -26,7 +26,7 @@ public class QRCodeText { private QRCodeText(Bill bill) { this.bill = bill; textBuilder = new StringBuilder(); - dataSeparator = bill.getFormat().getQrDataSeparator() == QrDataSeparator.CR_LF ? "\r\n" : "\n"; + dataSeparator = bill.getSeparator() == QrDataSeparator.CR_LF ? "\r\n" : "\n"; } /** @@ -141,7 +141,7 @@ public static Bill decode(String text) { Bill bill = new Bill(); bill.setVersion(Bill.Version.V2_0); - bill.getFormat().setQrDataSeparator(text.contains("\r\n") ? QrDataSeparator.CR_LF : QrDataSeparator.LF); + bill.setSeparator(text.contains("\r\n") ? QrDataSeparator.CR_LF : QrDataSeparator.LF); bill.setAccount(lines[3]); diff --git a/generator/src/main/java/net/codecrete/qrbill/generator/StringCleanup.java b/generator/src/main/java/net/codecrete/qrbill/generator/StringCleanup.java index 3794bce..5fcfe8a 100644 --- a/generator/src/main/java/net/codecrete/qrbill/generator/StringCleanup.java +++ b/generator/src/main/java/net/codecrete/qrbill/generator/StringCleanup.java @@ -251,9 +251,9 @@ private static boolean replaceCodePoint(int codePoint, SPSCharacterSet character String codePointString = new String(new int[] { codePoint }, 0, 1); // check if canonical decomposition yields a valid string - String canoncial = decomposedString(codePointString, characterSet, Normalizer.Form.NFD); - if (canoncial != null) { - sb.append(canoncial); + String canonical = decomposedString(codePointString, characterSet, Normalizer.Form.NFD); + if (canonical != null) { + sb.append(canonical); return true; } diff --git a/generator/src/main/java/net/codecrete/qrbill/generator/Validator.java b/generator/src/main/java/net/codecrete/qrbill/generator/Validator.java index d2370d3..8f33157 100644 --- a/generator/src/main/java/net/codecrete/qrbill/generator/Validator.java +++ b/generator/src/main/java/net/codecrete/qrbill/generator/Validator.java @@ -47,6 +47,8 @@ private ValidationResult validateBill() { billOut.setFormat(billIn.getFormat() != null ? new BillFormat(billIn.getFormat()) : null); billOut.setVersion(billIn.getVersion()); + billOut.setSeparator(billIn.getSeparator()); + billOut.setCharacterSet(billIn.getCharacterSet()); validateAccountNumber(); validateCreditor(); @@ -403,7 +405,7 @@ private String clippedValue(String value, int maxLength, String fieldRoot, Strin private String cleanedValue(String value, String fieldRoot, String subfield) { CleaningResult result = new CleaningResult(); - StringCleanup.cleanText(value, billOut.getFormat().getCharacterSet(), true, result); + StringCleanup.cleanText(value, billOut.getCharacterSet(), true, result); if (result.replacedUnsupportedChars) validationResult.addMessage(Type.WARNING, fieldRoot + subfield, ValidationConstants.KEY_REPLACED_UNSUPPORTED_CHARACTERS); return result.cleanedString; @@ -411,7 +413,7 @@ private String cleanedValue(String value, String fieldRoot, String subfield) { private String cleanedValue(String value, String fieldName) { CleaningResult result = new CleaningResult(); - StringCleanup.cleanText(value, billOut.getFormat().getCharacterSet(), true, result); + StringCleanup.cleanText(value, billOut.getCharacterSet(), true, result); if (result.replacedUnsupportedChars) validationResult.addMessage(Type.WARNING, fieldName, ValidationConstants.KEY_REPLACED_UNSUPPORTED_CHARACTERS); return result.cleanedString; diff --git a/generator/src/test/java/net/codecrete/qrbill/generator/A4BillTest.java b/generator/src/test/java/net/codecrete/qrbill/generator/A4BillTest.java index 80f8035..b0cff21 100644 --- a/generator/src/test/java/net/codecrete/qrbill/generator/A4BillTest.java +++ b/generator/src/test/java/net/codecrete/qrbill/generator/A4BillTest.java @@ -94,7 +94,7 @@ void createA4PDFBill8a() { @Test void createA4PDFBill8b() { Bill bill = SampleData.getExample8(); - bill.getFormat().setCharacterSet(SPSCharacterSet.EXTENDED_LATIN); + bill.setCharacterSet(SPSCharacterSet.EXTENDED_LATIN); generateAndCompareBill(bill, GraphicsFormat.PDF, "a4bill_ex8b.pdf"); } diff --git a/generator/src/test/java/net/codecrete/qrbill/generator/BillFormatTest.java b/generator/src/test/java/net/codecrete/qrbill/generator/BillFormatTest.java index d15daf7..2bc8911 100644 --- a/generator/src/test/java/net/codecrete/qrbill/generator/BillFormatTest.java +++ b/generator/src/test/java/net/codecrete/qrbill/generator/BillFormatTest.java @@ -25,24 +25,13 @@ void defaultValueTest() { assertEquals(144, format.getResolution()); assertEquals(5.0, format.getMarginLeft()); assertEquals(5.0, format.getMarginRight()); - assertEquals(QrDataSeparator.LF, format.getQrDataSeparator()); - assertEquals(SPSCharacterSet.LATIN_1_SUBSET, format.getCharacterSet()); - } - - @Test - void hashCodeTest() { - BillFormat format1 = new BillFormat(); - BillFormat format2 = new BillFormat(); - assertEquals(format1.hashCode(), format2.hashCode()); - format1.setCharacterSet(SPSCharacterSet.FULL_UNICODE); - assertNotEquals(format1.hashCode(), format2.hashCode()); } @Test void toStringTest() { BillFormat format = new BillFormat(); String text = format.toString(); - assertEquals("BillFormat{outputSize=QR_BILL_ONLY, language=EN, separatorType=DASHED_LINE_WITH_SCISSORS, fontFamily='Helvetica,Arial,\"Liberation Sans\"', graphicsFormat=SVG, resolution=144, marginLeft=5.0, marginRight=5.0, localCountryCode='CH', qrDataSeparator=LF, characterSet=LATIN_1_SUBSET}", text); + assertEquals("BillFormat{outputSize=QR_BILL_ONLY, language=EN, separatorType=DASHED_LINE_WITH_SCISSORS, fontFamily='Helvetica,Arial,\"Liberation Sans\"', graphicsFormat=SVG, resolution=144, marginLeft=5.0, marginRight=5.0, localCountryCode='CH'}", text); } @Test diff --git a/generator/src/test/java/net/codecrete/qrbill/generator/BillTest.java b/generator/src/test/java/net/codecrete/qrbill/generator/BillTest.java index f2c10ae..253bbe2 100644 --- a/generator/src/test/java/net/codecrete/qrbill/generator/BillTest.java +++ b/generator/src/test/java/net/codecrete/qrbill/generator/BillTest.java @@ -20,6 +20,23 @@ */ @DisplayName("Bill class") class BillTest { + @Test + void defaultValueTest() { + Bill bill = new Bill(); + assertNull(bill.getAmount()); + assertEquals("CHF", bill.getCurrency()); + assertNull(bill.getAccount()); + assertEquals(new Address(), bill.getCreditor()); + assertEquals(Bill.REFERENCE_TYPE_NO_REF, bill.getReferenceType()); + assertNull(bill.getReference()); + assertNull(bill.getDebtor()); + assertNull(bill.getUnstructuredMessage()); + assertNull(bill.getBillInformation()); + assertNull(bill.getAlternativeSchemes()); + assertEquals(new BillFormat(), bill.getFormat()); + assertEquals(QrDataSeparator.LF, bill.getSeparator()); + assertEquals(SPSCharacterSet.LATIN_1_SUBSET, bill.getCharacterSet()); + } @Test void setLanguage() { @@ -153,6 +170,20 @@ void setAlternativeScheme() { assertArrayEquals(createAlternativeSchemes(), bill.getAlternativeSchemes()); } + @Test + void setSeparator() { + Bill bill = new Bill(); + bill.setSeparator(QrDataSeparator.CR_LF); + assertEquals(QrDataSeparator.CR_LF, bill.getSeparator()); + } + + @Test + void setCharacterSet() { + Bill bill = new Bill(); + bill.setCharacterSet(SPSCharacterSet.EXTENDED_LATIN); + assertEquals(SPSCharacterSet.EXTENDED_LATIN, bill.getCharacterSet()); + } + @Test void testEqualsTrivial() { Bill bill = new Bill(); @@ -183,7 +214,7 @@ void testHashCode() { void testToString() { Bill bill = createBill(); String text = bill.toString(); - assertEquals("Bill{version=V2_0, amount=100.30, currency='CHF', account='CH12343345345', creditor=Address{type=STRUCTURED, name='Vision Consult GmbH', addressLine1='null', addressLine2='null', street='Hintergasse', houseNo='7b', postalCode='8400', town='Winterthur', countryCode='CH'}, referenceType='NON', reference='null', debtor=Address{type=STRUCTURED, name='Vision Consult GmbH', addressLine1='null', addressLine2='null', street='Hintergasse', houseNo='7b', postalCode='8400', town='Winterthur', countryCode='CH'}, unstructuredMessage='null', billInformation='null', alternativeSchemes=null, format=BillFormat{outputSize=QR_BILL_ONLY, language=EN, separatorType=DASHED_LINE_WITH_SCISSORS, fontFamily='Helvetica,Arial,\"Liberation Sans\"', graphicsFormat=SVG, resolution=144, marginLeft=5.0, marginRight=5.0, localCountryCode='CH', qrDataSeparator=LF, characterSet=LATIN_1_SUBSET}}", text); + assertEquals("Bill{version=V2_0, amount=100.30, currency='CHF', account='CH12343345345', creditor=Address{type=STRUCTURED, name='Vision Consult GmbH', addressLine1='null', addressLine2='null', street='Hintergasse', houseNo='7b', postalCode='8400', town='Winterthur', countryCode='CH'}, referenceType='NON', reference='null', debtor=Address{type=STRUCTURED, name='Vision Consult GmbH', addressLine1='null', addressLine2='null', street='Hintergasse', houseNo='7b', postalCode='8400', town='Winterthur', countryCode='CH'}, unstructuredMessage='null', billInformation='null', alternativeSchemes=null, format=BillFormat{outputSize=QR_BILL_ONLY, language=EN, separatorType=DASHED_LINE_WITH_SCISSORS, fontFamily='Helvetica,Arial,\"Liberation Sans\"', graphicsFormat=SVG, resolution=144, marginLeft=5.0, marginRight=5.0, localCountryCode='CH'}, qrDataSeparator=LF, characterSet=LATIN_1_SUBSET}", text); } private Address createAddress() { diff --git a/generator/src/test/java/net/codecrete/qrbill/generator/CharacterSetTest.java b/generator/src/test/java/net/codecrete/qrbill/generator/CharacterSetTest.java index 1fb164d..c8651ff 100644 --- a/generator/src/test/java/net/codecrete/qrbill/generator/CharacterSetTest.java +++ b/generator/src/test/java/net/codecrete/qrbill/generator/CharacterSetTest.java @@ -60,7 +60,7 @@ void unstructuredMessageReplacementLatin1Subset() { @Test void unstructuredMessageReplacementExtendedLatin() { bill = SampleData.getExample8(); - bill.getFormat().setCharacterSet(SPSCharacterSet.EXTENDED_LATIN); + bill.setCharacterSet(SPSCharacterSet.EXTENDED_LATIN); validate(); assertNoMessages(); } diff --git a/generator/src/test/java/net/codecrete/qrbill/generator/DecodedTextTest.java b/generator/src/test/java/net/codecrete/qrbill/generator/DecodedTextTest.java index a239bb0..547f6fc 100644 --- a/generator/src/test/java/net/codecrete/qrbill/generator/DecodedTextTest.java +++ b/generator/src/test/java/net/codecrete/qrbill/generator/DecodedTextTest.java @@ -68,7 +68,7 @@ void decodeText4() { void decodeTextNewline(int sample, String newLine, boolean extraNewLine) { QrDataSeparator separator = newLine.equals("\r\n") ? QrDataSeparator.CR_LF : QrDataSeparator.LF; Bill bill = SampleQrCodeText.getBillData(sample); - bill.getFormat().setQrDataSeparator(separator); + bill.setSeparator(separator); normalizeSourceBill(bill); Bill bill2 = QRBill.decodeQrCodeText(SampleQrCodeText.getQrCodeText(sample, newLine) + (extraNewLine ? newLine : "")); normalizeDecodedBill(bill2); @@ -84,6 +84,7 @@ void decodeInvalidRefType() { assertEquals("XXXX", bill2.getReferenceType()); } + @SuppressWarnings("java:S1066") static void normalizeSourceBill(Bill bill) { bill.getFormat().setLanguage(Language.DE); bill.setAccount(bill.getAccount().replace(" ", "")); diff --git a/generator/src/test/java/net/codecrete/qrbill/generator/EncodedTextTest.java b/generator/src/test/java/net/codecrete/qrbill/generator/EncodedTextTest.java index 57a9e09..df94a8d 100644 --- a/generator/src/test/java/net/codecrete/qrbill/generator/EncodedTextTest.java +++ b/generator/src/test/java/net/codecrete/qrbill/generator/EncodedTextTest.java @@ -28,7 +28,7 @@ class EncodedTextTest { @MethodSource("provideNewLineSampleCombinations") void createText(int sample, QrDataSeparator separator) { Bill bill = SampleQrCodeText.getBillData(sample); - bill.getFormat().setQrDataSeparator(separator); + bill.setSeparator(separator); assertEquals( SampleQrCodeText.getQrCodeText(sample, separator == QrDataSeparator.CR_LF ? "\r\n" : "\n"), QRBill.encodeQrCodeText(bill)