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 6d9b6c8..28e0479 100644 --- a/generator/src/main/java/net/codecrete/qrbill/generator/QRCodeText.java +++ b/generator/src/main/java/net/codecrete/qrbill/generator/QRCodeText.java @@ -177,6 +177,7 @@ public static Bill decode(String text) { return bill; } + @SuppressWarnings("java:S1066") private static void validateHeader(String[] lines) { if (lines.length < 31 || lines.length > 34) { // A line feed at the end is illegal (cf 4.2.3) but found in practice. Don't be too strict. diff --git a/generator/src/test/java/net/codecrete/qrbill/canvas/PdfCanvasTest.java b/generator/src/test/java/net/codecrete/qrbill/canvas/PdfCanvasTest.java index 02e13a0..8a20b7e 100644 --- a/generator/src/test/java/net/codecrete/qrbill/canvas/PdfCanvasTest.java +++ b/generator/src/test/java/net/codecrete/qrbill/canvas/PdfCanvasTest.java @@ -23,8 +23,10 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; /** * Unit tests for generating QR bills as PDF @@ -51,36 +53,45 @@ void pdfWriteTo() throws IOException { void pdfSaveAs() throws IOException { Bill bill = SampleData.getExample4(); Path path = Files.createTempFile("pdfcanvas-", ".pdf"); - try (PDFCanvas canvas = new PDFCanvas(QRBill.A4_PORTRAIT_WIDTH, QRBill.A4_PORTRAIT_HEIGHT)) { - QRBill.draw(bill, canvas); - canvas.saveAs(path); - } + try { + try (PDFCanvas canvas = new PDFCanvas(QRBill.A4_PORTRAIT_WIDTH, QRBill.A4_PORTRAIT_HEIGHT)) { + QRBill.draw(bill, canvas); + canvas.saveAs(path); + } - compareResult(path, "pdfcanvas-saveas.pdf"); + compareResult(path, "pdfcanvas-saveas.pdf"); + } finally { + Files.deleteIfExists(path); + } } @Test void addPageToOpenPdfDocument() throws IOException { - PDDocument document = new PDDocument(); - PDPage page = new PDPage(new PDRectangle(210 * MM_TO_PT, 297 * MM_TO_PT)); - document.addPage(page); - try (PDPageContentStream stream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.OVERWRITE, true)) { - stream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD), 18); - stream.beginText(); - stream.newLineAtOffset(20 * MM_TO_PT, 220 * MM_TO_PT); - stream.showText("Swiss QR Bill"); - stream.endText(); - } + try (PDDocument document = new PDDocument()) { + PDPage page = new PDPage(new PDRectangle(210 * MM_TO_PT, 297 * MM_TO_PT)); + document.addPage(page); + try (PDPageContentStream stream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.OVERWRITE, true)) { + stream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD), 18); + stream.beginText(); + stream.newLineAtOffset(20 * MM_TO_PT, 220 * MM_TO_PT); + stream.showText("Swiss QR Bill"); + stream.endText(); + } - Bill bill = SampleData.getExample1(); - bill.getFormat().setOutputSize(OutputSize.QR_BILL_EXTRA_SPACE); - try (PDFCanvas canvas = new PDFCanvas(document, PDFCanvas.NEW_PAGE_AT_END)) { - QRBill.draw(bill, canvas); - } + Bill bill = SampleData.getExample1(); + bill.getFormat().setOutputSize(OutputSize.QR_BILL_EXTRA_SPACE); + try (PDFCanvas canvas = new PDFCanvas(document, PDFCanvas.NEW_PAGE_AT_END)) { + QRBill.draw(bill, canvas); + } - Path path = Files.createTempFile("pdfcanvas-", ".pdf"); - document.save(path.toFile()); - compareResult(path, "pdfcanvas-opendoc.pdf"); + Path path = Files.createTempFile("pdfcanvas-", ".pdf"); + try { + document.save(path.toFile()); + compareResult(path, "pdfcanvas-opendoc.pdf"); + } finally { + Files.deleteIfExists(path); + } + } } @Test @@ -91,39 +102,77 @@ void coloredLines() throws IOException { canvas.lineTo(90, 90); canvas.strokePath(2, 0xFF00FF, Canvas.LineStyle.Dashed, true); canvas.saveAs(path); - } - compareResult(path, "pdfcanvas-colored-line.pdf"); + compareResult(path, "pdfcanvas-colored-line.pdf"); + } finally { + Files.deleteIfExists(path); + } } @Test void addPageToOpenContentStream() throws IOException { - PDDocument document = new PDDocument(); - PDPage page = new PDPage(new PDRectangle(210 * MM_TO_PT, 297 * MM_TO_PT)); - document.addPage(page); - try (PDPageContentStream stream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.OVERWRITE, true)) { - // offset from bottom - stream.saveGraphicsState(); - stream.transform(Matrix.getTranslateInstance(0, 20 * MM_TO_PT)); + try (PDDocument document = new PDDocument()) { + PDPage page = new PDPage(new PDRectangle(210 * MM_TO_PT, 297 * MM_TO_PT)); + document.addPage(page); + try (PDPageContentStream stream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.OVERWRITE, true)) { + // offset from bottom + stream.saveGraphicsState(); + stream.transform(Matrix.getTranslateInstance(0, 20 * MM_TO_PT)); + + Bill bill = SampleData.getExample1(); + bill.getFormat().setOutputSize(OutputSize.QR_BILL_EXTRA_SPACE); + try (PDFCanvas canvas = new PDFCanvas(stream)) { + QRBill.draw(bill, canvas); + } + + stream.restoreGraphicsState(); + + stream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD), 18); + stream.beginText(); + stream.newLineAtOffset(20 * MM_TO_PT, 220 * MM_TO_PT); + stream.showText("Swiss QR Bill"); + stream.endText(); + } + + Path path = Files.createTempFile("pdfcanvas-", ".pdf"); + try { + document.save(path.toFile()); + compareResult(path, "pdfcanvas-openstream.pdf"); + } finally { + Files.deleteIfExists(path); + } + } + } + @Test + void customFonts() throws IOException { + Path regularFontPath = unpackFont("/fonts/LiberationSans-Regular.ttf"); + Path boldFontPath = unpackFont("/fonts/LiberationSans-Bold.ttf"); + try { Bill bill = SampleData.getExample1(); - bill.getFormat().setOutputSize(OutputSize.QR_BILL_EXTRA_SPACE); - try (PDFCanvas canvas = new PDFCanvas(stream)) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + try (PDFCanvas canvas = new PDFCanvas(QRBill.A4_PORTRAIT_WIDTH, QRBill.A4_PORTRAIT_HEIGHT, + PDFFontSettings.embeddedCustomFont("Liberation Sans", regularFontPath, boldFontPath))) { QRBill.draw(bill, canvas); + canvas.writeTo(os); } + byte[] data = os.toByteArray(); + compareResult(data, "pdfcanvas-custom-font.pdf"); - stream.restoreGraphicsState(); - - stream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD), 18); - stream.beginText(); - stream.newLineAtOffset(20 * MM_TO_PT, 220 * MM_TO_PT); - stream.showText("Swiss QR Bill"); - stream.endText(); + } finally { + Files.delete(regularFontPath); + Files.delete(boldFontPath); } - Path path = Files.createTempFile("pdfcanvas-", ".pdf"); - document.save(path.toFile()); - compareResult(path, "pdfcanvas-openstream.pdf"); + } + + @SuppressWarnings("DataFlowIssue") + private Path unpackFont(String fontResource) throws IOException { + Path fontPath = Files.createTempFile("font-", ".ttf"); + try (InputStream is = PdfCanvasTest.class.getResource(fontResource).openStream()) { + Files.copy(is, fontPath, StandardCopyOption.REPLACE_EXISTING); + } + return fontPath; } private void compareResult(byte[] imageData, String expectedFileName) { diff --git a/generator/src/test/java/net/codecrete/qrbill/generator/PaymentsCharacterSetTest.java b/generator/src/test/java/net/codecrete/qrbill/generator/PaymentsCharacterSetTest.java index 41e6c01..7fd3cdb 100644 --- a/generator/src/test/java/net/codecrete/qrbill/generator/PaymentsCharacterSetTest.java +++ b/generator/src/test/java/net/codecrete/qrbill/generator/PaymentsCharacterSetTest.java @@ -71,6 +71,7 @@ void blankText_returnsNull(String text) { @ParameterizedTest @ValueSource(strings = { "a b c|a b c", " a b c|a b c"}) + @SuppressWarnings("java:S4144") void multipleSpaces_becomeSingleSpace(String combinedText) { String[] textAndExpected = combinedText.split("\\|"); assertEquals(textAndExpected[1], Payments.cleanedAndTrimmedText(textAndExpected[0], EXTENDED_LATIN)); @@ -104,10 +105,7 @@ private static Stream provideExtendedLatinChars() { continue; builder.add(Arguments.of(ch, String.format("U+%04X", (int) ch))); } - for (char ch = 0x00A0; ch <= 0x00FF; ch++) { - builder.add(Arguments.of(ch, String.format("U+%04X", (int) ch))); - } - for (char ch = 0x0100; ch <= 0x017F; ch++) { + for (char ch = 0x00A0; ch <= 0x017F; ch++) { builder.add(Arguments.of(ch, String.format("U+%04X", (int) ch))); } builder.add(Arguments.of((char) 0x0218, "U+0218")); diff --git a/generator/src/test/java/net/codecrete/qrbill/generator/SPSCharacterSetTest.java b/generator/src/test/java/net/codecrete/qrbill/generator/SPSCharacterSetTest.java index 5fb0c67..6bbdc53 100644 --- a/generator/src/test/java/net/codecrete/qrbill/generator/SPSCharacterSetTest.java +++ b/generator/src/test/java/net/codecrete/qrbill/generator/SPSCharacterSetTest.java @@ -13,6 +13,12 @@ @DisplayName("SPS character set") class SPSCharacterSetTest { + @ParameterizedTest + @ValueSource(chars = { '\t', '^', '\u007F', '¡', '¶', 'Õ', 'æ', 'ø', '€' }) + void latin1Subset_doesNotContainInvalidCharacters(char validChar) { + assertFalse(SPSCharacterSet.LATIN_1_SUBSET.contains(validChar)); + } + @ParameterizedTest @ValueSource(chars = { 'A', 'b', '3', '%', '{', '®', 'Ò', 'æ', 'Ă', 'Ķ', 'Ŕ', 'ț', '€' }) void extendedLatin_containsValidCharacters(char validChar) { @@ -36,4 +42,5 @@ void extendedLatin_doesNotContainInvalidCharacters(char invalidChar) { void extendedLatin_doesNotContainInvalidCodePoints(char invalidChar) { assertFalse(SPSCharacterSet.EXTENDED_LATIN.contains((int)invalidChar)); } + } diff --git a/generator/src/test/resources/pdfcanvas-custom-font.pdf b/generator/src/test/resources/pdfcanvas-custom-font.pdf new file mode 100644 index 0000000..4bf7c31 Binary files /dev/null and b/generator/src/test/resources/pdfcanvas-custom-font.pdf differ