From bb1ee74238ba4b4fe0dda5b45a0d42b62c63e3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20Th=C3=A0nh=20=C4=90=E1=BA=A1t?= <91755733+lds217@users.noreply.github.com> Date: Mon, 4 Aug 2025 00:03:01 +0700 Subject: [PATCH] [update] Refactor CheckUpPage and related components to enhance PDF generation and error handling. Remove unused PDF dependencies and streamline Jasper report loading. Update project configuration for JDK version and adjust run configurations for improved compatibility. --- .idea/misc.xml | 2 +- .idea/runConfigurations/BSK_Server.xml | 13 +- database/BSK.db | Bin 1339392 -> 1339392 bytes pom.xml | 13 +- .../ui/component/CheckUpPage/CheckUpPage.java | 43 +- .../PrintDialog/BarcodeGenerator.java | 410 +++++++++++ .../{print_forms => }/InvoiceItem.java | 2 +- .../PrintDialog/MedicineInvoice.java | 269 +++++--- .../PrintDialog/UltrasoundResult.java | 643 ++++-------------- .../print_forms/medserinvoice.jasper | Bin 60690 -> 0 bytes .../print_forms/test_ultrasound_print.java | 115 ---- .../print_forms/ultrasoundresult.jasper | Bin 43444 -> 0 bytes .../server/network/handler/ServerHandler.java | 29 +- .../print_forms/medserinvoice.jrxml | 42 +- .../print_forms/ultrasoundresult.jrxml | 0 .../ultrasoundresult_potrait.jrxml | 0 temp-jar-extract/META-INF/MANIFEST.MF | 5 + temp/barcodes/invoice_barcode.png | Bin 0 -> 103 bytes temp/qrcodes/invoice_qrcode.png | Bin 0 -> 519 bytes 19 files changed, 788 insertions(+), 798 deletions(-) create mode 100644 src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/BarcodeGenerator.java rename src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/{print_forms => }/InvoiceItem.java (97%) delete mode 100644 src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jasper delete mode 100644 src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/test_ultrasound_print.java delete mode 100644 src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jasper rename src/main/{java/BsK/client/ui/component/CheckUpPage/PrintDialog => resources}/print_forms/medserinvoice.jrxml (95%) rename src/main/{java/BsK/client/ui/component/CheckUpPage/PrintDialog => resources}/print_forms/ultrasoundresult.jrxml (100%) rename src/main/{java/BsK/client/ui/component/CheckUpPage/PrintDialog => resources}/print_forms/ultrasoundresult_potrait.jrxml (100%) create mode 100644 temp-jar-extract/META-INF/MANIFEST.MF create mode 100644 temp/barcodes/invoice_barcode.png create mode 100644 temp/qrcodes/invoice_qrcode.png diff --git a/.idea/misc.xml b/.idea/misc.xml index 6546f14..9dfa497 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,5 +8,5 @@ - + \ No newline at end of file diff --git a/.idea/runConfigurations/BSK_Server.xml b/.idea/runConfigurations/BSK_Server.xml index dcad2e6..dedfb31 100644 --- a/.idea/runConfigurations/BSK_Server.xml +++ b/.idea/runConfigurations/BSK_Server.xml @@ -1,10 +1,11 @@ - + + + \ No newline at end of file + + \ No newline at end of file diff --git a/database/BSK.db b/database/BSK.db index b45b7ca9eaba36b765e4fa8195557f4c634d1100..3937548427e9a466be973e8bf7c8d6ec942ae02e 100644 GIT binary patch delta 252 zcmZp85ZLe_aDp`B;fXTNjE6TS27A5Hg8zZ*dQjvt;oR0$iyox$;)eGU}UOmV4-VZ+Gy!fS47C z*?^cGh&h0m6NtICAJ^x$Kftn*fpz6(L4*0MYEwd(rC5P>F!1sUE)7){ZB%CE;NTE$ zl=WxiGpB{`>Gcqb@1-8EmBf4b9uZeNyg#f|k-&x>fc g@B7aU#5_RE3&eat%n!r@Kr9HvLfiNK7j`ND08PAA>Hq)$ delta 272 zcmZp85ZLe_aDp`Bfr&ECj0ZL*2@|;=bH`tiWGUpxEUE3 zIh}c#m>7BaH*Z+a*dWHwt;oR0$iyox$;)eGU}UOmV4-VZ)P7u_5r~<9m>Gy!fS47C z*?^cGh&h0m6NtICAJ^x$Kftnsfpy1bL4$RyYExX9rC5P>F!1sUE;UycZB%CE;NTE$ zl=Wxi z*L2r>VgBh(|G9lxVhnm$O+7E7-M;TXHxTmxF)tAF0Wm)i3jnbo5DRVJ_g~nl0084i BUQqx5 diff --git a/pom.xml b/pom.xml index eebabc2..3154827 100644 --- a/pom.xml +++ b/pom.xml @@ -138,17 +138,6 @@ swingx 1.6.1 - - org.apache.pdfbox - pdfbox - 2.0.27 - - - com.itextpdf - itext7-core - 7.1.12 - pom - com.github.sarxos webcam-capture @@ -248,4 +237,4 @@ 5.2.5 - + \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java index 770bd38..dc0772d 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java @@ -1819,28 +1819,33 @@ public class CheckUpPage extends JPanel { supplements.toArray(new String[0][]) // Pass supplements ); // First, show the print preview to the user - medicineInvoice.showDirectJasperViewer(); + try { + medicineInvoice.showDirectJasperViewer(); - // Then, asynchronously generate the PDF bytes and upload them - medicineInvoice.generatePdfBytesAsync().thenAccept(pdfBytes -> { - if (pdfBytes != null && pdfBytes.length > 0) { - String checkupId = checkupIdField.getText(); - String fileName = "medserinvoice.pdf"; - String pdfType = "medserinvoice"; + // Then, asynchronously generate the PDF bytes and upload them + // medicineInvoice.generatePdfBytesAsync().thenAccept(pdfBytes -> { + // if (pdfBytes != null && pdfBytes.length > 0) { + // String checkupId = checkupIdField.getText(); + // String fileName = "medserinvoice.pdf"; + // String pdfType = "medserinvoice"; - log.info("Uploading {} ({}) for checkupId: {}", fileName, pdfType, checkupId); + // log.info("Uploading {} ({}) for checkupId: {}", fileName, pdfType, checkupId); - UploadCheckupPdfRequest request = new UploadCheckupPdfRequest(checkupId, pdfBytes, fileName, pdfType); - NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request); - } - }).exceptionally(ex -> { - log.error("Failed to generate or upload medicine invoice PDF", ex); - // Show error in the UI thread - SwingUtilities.invokeLater(() -> - JOptionPane.showMessageDialog(this, "Lỗi khi tạo hoặc tải lên file PDF hóa đơn: " + ex.getMessage(), "Lỗi PDF", JOptionPane.ERROR_MESSAGE) - ); - return null; - }); + // UploadCheckupPdfRequest request = new UploadCheckupPdfRequest(checkupId, pdfBytes, fileName, pdfType); + // NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request); + // } + // }).exceptionally(ex -> { + // log.error("Failed to generate or upload medicine invoice PDF", ex); + // // Show error in the UI thread + // SwingUtilities.invokeLater(() -> + // JOptionPane.showMessageDialog(this, "Lỗi khi tạo hoặc tải lên file PDF hóa đơn: " + ex.getMessage(), "Lỗi PDF", JOptionPane.ERROR_MESSAGE) + // ); + // return null; + // }); + } catch (Exception e) { + log.error("Failed to show direct JasperViewer", e); + JOptionPane.showMessageDialog(this, "Lỗi khi hiển thị hộp thoại in: " + e.getMessage(), "Lỗi In", JOptionPane.ERROR_MESSAGE); + } break; case "ultrasound": // This case now handles "Lưu & In" // Step 1: Validate conditions first. diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/BarcodeGenerator.java b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/BarcodeGenerator.java new file mode 100644 index 0000000..d8aae31 --- /dev/null +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/BarcodeGenerator.java @@ -0,0 +1,410 @@ +package BsK.client.ui.component.CheckUpPage.PrintDialog; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.WriterException; +import com.google.zxing.client.j2se.MatrixToImageWriter; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.oned.Code128Writer; +import com.google.zxing.qrcode.QRCodeWriter; +import lombok.extern.slf4j.Slf4j; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; + +@Slf4j +public class BarcodeGenerator { + + // Fixed paths for saving images + private static final String BARCODE_DIRECTORY = "temp/barcodes"; + private static final String QRCODE_DIRECTORY = "temp/qrcodes"; + + // Ensure directories exist + static { + try { + Files.createDirectories(Paths.get(BARCODE_DIRECTORY)); + Files.createDirectories(Paths.get(QRCODE_DIRECTORY)); + log.info("Created barcode directories: {} and {}", BARCODE_DIRECTORY, QRCODE_DIRECTORY); + } catch (IOException e) { + log.error("Failed to create barcode directories", e); + } + } + + /** + * Generates a Code 128 barcode, saves it to a fixed path, and returns the file path. + * File is saved as: temp/barcodes/barcode_{text}_{timestamp}.png + */ + public static String generateCode128AndSave(String text, int width, int height) throws WriterException, IOException { + if (text == null || text.trim().isEmpty()) { + throw new IllegalArgumentException("Barcode text cannot be null or empty"); + } + + log.info("Generating Code 128 barcode and saving to file for text: {}", text); + + Code128Writer writer = new Code128Writer(); + BitMatrix bitMatrix = writer.encode(text, BarcodeFormat.CODE_128, width, height); + BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix); + + // Create filename with timestamp to avoid conflicts + String sanitizedText = sanitizeFileName(text); + String filename = String.format("barcode_%s_%d.png", sanitizedText, System.currentTimeMillis()); + String filePath = Paths.get(BARCODE_DIRECTORY, filename).toString(); + + // Save image to file + File outputFile = new File(filePath); + ImageIO.write(image, "PNG", outputFile); + + log.info("Successfully saved Code 128 barcode to: {}", filePath); + return filePath; + } + + /** + * Generates a QR Code, saves it to a fixed path, and returns the file path. + * File is saved as: temp/qrcodes/qrcode_{text}_{timestamp}.png + */ + public static String generateQRCodeAndSave(String text, int width, int height) throws WriterException, IOException { + if (text == null || text.trim().isEmpty()) { + throw new IllegalArgumentException("QR Code text cannot be null or empty"); + } + + log.info("Generating QR Code and saving to file for text: {}", text); + + QRCodeWriter writer = new QRCodeWriter(); + BitMatrix bitMatrix = writer.encode(text, BarcodeFormat.QR_CODE, width, height); + BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix); + + // Create filename with timestamp to avoid conflicts + String sanitizedText = sanitizeFileName(text); + String filename = String.format("qrcode_%s_%d.png", sanitizedText, System.currentTimeMillis()); + String filePath = Paths.get(QRCODE_DIRECTORY, filename).toString(); + + // Save image to file + File outputFile = new File(filePath); + ImageIO.write(image, "PNG", outputFile); + + log.info("Successfully saved QR Code to: {}", filePath); + return filePath; + } + + /** + * Generates a Code 128 barcode with custom directory, saves it, and returns the file path. + */ + public static String generateCode128AndSave(String text, int width, int height, String customDirectory) throws WriterException, IOException { + if (text == null || text.trim().isEmpty()) { + throw new IllegalArgumentException("Barcode text cannot be null or empty"); + } + + // Ensure custom directory exists + Files.createDirectories(Paths.get(customDirectory)); + + log.info("Generating Code 128 barcode and saving to custom directory: {}", customDirectory); + + Code128Writer writer = new Code128Writer(); + BitMatrix bitMatrix = writer.encode(text, BarcodeFormat.CODE_128, width, height); + BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix); + + // Create filename + String sanitizedText = sanitizeFileName(text); + String filename = String.format("barcode_%s_%d.png", sanitizedText, System.currentTimeMillis()); + String filePath = Paths.get(customDirectory, filename).toString(); + + // Save image to file + File outputFile = new File(filePath); + ImageIO.write(image, "PNG", outputFile); + + log.info("Successfully saved Code 128 barcode to: {}", filePath); + return filePath; + } + + /** + * Generates a QR Code with custom directory, saves it, and returns the file path. + */ + public static String generateQRCodeAndSave(String text, int width, int height, String customDirectory) throws WriterException, IOException { + if (text == null || text.trim().isEmpty()) { + throw new IllegalArgumentException("QR Code text cannot be null or empty"); + } + + // Ensure custom directory exists + Files.createDirectories(Paths.get(customDirectory)); + + log.info("Generating QR Code and saving to custom directory: {}", customDirectory); + + QRCodeWriter writer = new QRCodeWriter(); + BitMatrix bitMatrix = writer.encode(text, BarcodeFormat.QR_CODE, width, height); + BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix); + + // Create filename + String sanitizedText = sanitizeFileName(text); + String filename = String.format("qrcode_%s_%d.png", sanitizedText, System.currentTimeMillis()); + String filePath = Paths.get(customDirectory, filename).toString(); + + // Save image to file + File outputFile = new File(filePath); + ImageIO.write(image, "PNG", outputFile); + + log.info("Successfully saved QR Code to: {}", filePath); + return filePath; + } + + /** + * Generates a Code 128 barcode with fixed filename (overwrites existing), saves it, and returns the file path. + * Useful when you want to reuse the same filename for reports. + */ + public static String generateCode128WithFixedName(String text, int width, int height, String filename) throws WriterException, IOException { + if (text == null || text.trim().isEmpty()) { + throw new IllegalArgumentException("Barcode text cannot be null or empty"); + } + + log.info("Generating Code 128 barcode with fixed filename: {}", filename); + + Code128Writer writer = new Code128Writer(); + BitMatrix bitMatrix = writer.encode(text, BarcodeFormat.CODE_128, width, height); + BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix); + + // Ensure filename has .png extension + if (!filename.toLowerCase().endsWith(".png")) { + filename += ".png"; + } + + String filePath = Paths.get(BARCODE_DIRECTORY, filename).toString(); + + // Save image to file (will overwrite if exists) + File outputFile = new File(filePath); + ImageIO.write(image, "PNG", outputFile); + + log.info("Successfully saved Code 128 barcode to: {}", filePath); + return filePath; + } + + /** + * Generates a QR Code with fixed filename (overwrites existing), saves it, and returns the file path. + * Useful when you want to reuse the same filename for reports. + */ + public static String generateQRCodeWithFixedName(String text, int width, int height, String filename) throws WriterException, IOException { + if (text == null || text.trim().isEmpty()) { + throw new IllegalArgumentException("QR Code text cannot be null or empty"); + } + + log.info("Generating QR Code with fixed filename: {}", filename); + + QRCodeWriter writer = new QRCodeWriter(); + BitMatrix bitMatrix = writer.encode(text, BarcodeFormat.QR_CODE, width, height); + BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix); + + // Ensure filename has .png extension + if (!filename.toLowerCase().endsWith(".png")) { + filename += ".png"; + } + + String filePath = Paths.get(QRCODE_DIRECTORY, filename).toString(); + + // Save image to file (will overwrite if exists) + File outputFile = new File(filePath); + ImageIO.write(image, "PNG", outputFile); + + log.info("Successfully saved QR Code to: {}", filePath); + return filePath; + } + + /** + * Utility method to clean up temporary barcode files older than specified minutes + */ + public static void cleanupOldBarcodeFiles(int olderThanMinutes) { + cleanupOldFiles(BARCODE_DIRECTORY, olderThanMinutes); + cleanupOldFiles(QRCODE_DIRECTORY, olderThanMinutes); + } + + /** + * Helper method to clean up old files in a directory + */ + private static void cleanupOldFiles(String directory, int olderThanMinutes) { + try { + File dir = new File(directory); + if (!dir.exists()) return; + + long cutoffTime = System.currentTimeMillis() - (olderThanMinutes * 60 * 1000L); + + File[] files = dir.listFiles((file) -> + file.isFile() && + file.getName().toLowerCase().endsWith(".png") && + file.lastModified() < cutoffTime + ); + + if (files != null) { + for (File file : files) { + if (file.delete()) { + log.debug("Deleted old barcode file: {}", file.getName()); + } + } + log.info("Cleaned up {} old files from {}", files.length, directory); + } + } catch (Exception e) { + log.warn("Failed to cleanup old files in directory: {}", directory, e); + } + } + + /** + * Helper method to sanitize text for use in filenames + */ + private static String sanitizeFileName(String text) { + if (text == null) return "empty"; + + // Replace invalid filename characters and limit length + String sanitized = text.replaceAll("[^a-zA-Z0-9._-]", "_"); + if (sanitized.length() > 50) { + sanitized = sanitized.substring(0, 50); + } + return sanitized; + } + + // ============ ORIGINAL METHODS KEPT FOR BACKWARD COMPATIBILITY ============ + + /** + * Generates a Code 128 barcode and returns it as a Base64 encoded string. + * Format: data:image/png;base64,{base64_data} + */ + public static String generateCode128Base64String(String text, int width, int height) throws WriterException, IOException { + if (text == null || text.trim().isEmpty()) { + throw new IllegalArgumentException("Barcode text cannot be null or empty"); + } + + log.info("Generating Code 128 barcode as Base64 string for text: {}", text); + + Code128Writer writer = new Code128Writer(); + BitMatrix bitMatrix = writer.encode(text, BarcodeFormat.CODE_128, width, height); + + BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix); + String base64String = bufferedImageToBase64String(image); + + log.info("Successfully generated Code 128 barcode Base64 string"); + return base64String; + } + + /** + * Generates a QR Code and returns it as a Base64 encoded string. + * Format: data:image/png;base64,{base64_data} + */ + public static String generateQRCodeBase64String(String text, int width, int height) throws WriterException, IOException { + if (text == null || text.trim().isEmpty()) { + throw new IllegalArgumentException("QR Code text cannot be null or empty"); + } + + log.info("Generating QR Code as Base64 string for text: {}", text); + + QRCodeWriter writer = new QRCodeWriter(); + BitMatrix bitMatrix = writer.encode(text, BarcodeFormat.QR_CODE, width, height); + + BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix); + String base64String = bufferedImageToBase64String(image); + + log.info("Successfully generated QR Code Base64 string"); + return base64String; + } + + /** + * Generates a Code 128 barcode and returns it as a BufferedImage. + * This method is JAR-compatible and works better with JasperReports. + */ + public static BufferedImage generateCode128BufferedImage(String text, int width, int height) throws WriterException, IOException { + if (text == null || text.trim().isEmpty()) { + throw new IllegalArgumentException("Barcode text cannot be null or empty"); + } + + log.info("Generating Code 128 barcode as BufferedImage for text: {}", text); + + Code128Writer writer = new Code128Writer(); + BitMatrix bitMatrix = writer.encode(text, BarcodeFormat.CODE_128, width, height); + + BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix); + log.info("Successfully generated Code 128 barcode BufferedImage"); + + return image; + } + + /** + * Generates a QR Code and returns it as a BufferedImage. + * This method is JAR-compatible and works better with JasperReports. + */ + public static BufferedImage generateQRCodeBufferedImage(String text, int width, int height) throws WriterException, IOException { + if (text == null || text.trim().isEmpty()) { + throw new IllegalArgumentException("QR Code text cannot be null or empty"); + } + + log.info("Generating QR Code as BufferedImage for text: {}", text); + + QRCodeWriter writer = new QRCodeWriter(); + BitMatrix bitMatrix = writer.encode(text, BarcodeFormat.QR_CODE, width, height); + + BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix); + log.info("Successfully generated QR Code BufferedImage"); + + return image; + } + + /** + * Legacy method: Generates a Code 128 barcode and returns it as an InputStream. + * Keep this for backward compatibility, but prefer BufferedImage methods for JasperReports. + */ + public static InputStream generateCode128Stream(String text, int width, int height) throws WriterException, IOException { + BufferedImage image = generateCode128BufferedImage(text, width, height); + return bufferedImageToInputStream(image); + } + + /** + * Legacy method: Generates a QR Code and returns it as an InputStream. + * Keep this for backward compatibility, but prefer BufferedImage methods for JasperReports. + */ + public static InputStream generateQRCodeStream(String text, int width, int height) throws WriterException, IOException { + BufferedImage image = generateQRCodeBufferedImage(text, width, height); + return bufferedImageToInputStream(image); + } + + /** + * Helper method to convert BufferedImage to Base64 encoded string + * Returns format: data:image/png;base64,{base64_data} + */ + private static String bufferedImageToBase64String(BufferedImage image) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(image, "PNG", baos); + byte[] imageBytes = baos.toByteArray(); + String base64String = Base64.getEncoder().encodeToString(imageBytes); + return "data:image/png;base64," + base64String; + } + + /** + * Helper method to convert BufferedImage to InputStream + */ + private static InputStream bufferedImageToInputStream(BufferedImage image) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(image, "PNG", baos); + byte[] imageBytes = baos.toByteArray(); + return new ByteArrayInputStream(imageBytes); + } + + /** + * Helper method to convert Base64 string back to BufferedImage if needed + * Accepts both formats: "data:image/png;base64,{data}" or just "{data}" + */ + public static BufferedImage base64StringToBufferedImage(String base64String) throws IOException { + if (base64String == null || base64String.trim().isEmpty()) { + throw new IllegalArgumentException("Base64 string cannot be null or empty"); + } + + // Remove data URL prefix if present + String base64Data = base64String; + if (base64String.startsWith("data:image/")) { + int commaIndex = base64String.indexOf(','); + if (commaIndex != -1) { + base64Data = base64String.substring(commaIndex + 1); + } + } + + byte[] imageBytes = Base64.getDecoder().decode(base64Data); + ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes); + return ImageIO.read(bais); + } +} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/InvoiceItem.java b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/InvoiceItem.java similarity index 97% rename from src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/InvoiceItem.java rename to src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/InvoiceItem.java index c1bf161..d762766 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/InvoiceItem.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/InvoiceItem.java @@ -1,4 +1,4 @@ -package BsK.client.ui.component.CheckUpPage.PrintDialog.print_forms; +package BsK.client.ui.component.CheckUpPage.PrintDialog; /** * Represents a single item on the invoice, which can be a medicine, a service, or a supplement. diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java index 56daa64..9489b06 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java @@ -1,7 +1,7 @@ package BsK.client.ui.component.CheckUpPage.PrintDialog; import BsK.client.LocalStorage; -import BsK.client.ui.component.CheckUpPage.PrintDialog.print_forms.InvoiceItem; +import BsK.client.ui.component.CheckUpPage.PrintDialog.InvoiceItem; // JasperReports imports import net.sf.jasperreports.engine.*; import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; @@ -9,8 +9,6 @@ import net.sf.jasperreports.engine.design.JasperDesign; import net.sf.jasperreports.engine.xml.JRXmlLoader; import net.sf.jasperreports.view.JasperViewer; -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.rendering.PDFRenderer; import javax.swing.*; import java.awt.*; @@ -23,6 +21,7 @@ import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.io.*; import java.util.ArrayList; +import java.io.ByteArrayInputStream; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.text.DecimalFormat; @@ -30,8 +29,124 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.concurrent.CompletableFuture; import java.io.IOException; +import java.net.URL; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j public class MedicineInvoice{ + + /** + * Load and compile Jasper report from JRXML following standard pattern: inputstream → load → compile + */ + private static JasperReport loadCompiledReport(String reportName) { + String jrxmlPath = "/print_forms/" + reportName + ".jrxml"; + + try (InputStream inputStream = loadResourceAsStream(jrxmlPath)) { + if (inputStream == null) { + throw new RuntimeException("JRXML resource not found: " + jrxmlPath); + } + + log.info("Loading and compiling JRXML: {}", jrxmlPath); + + // Standard JasperReports pattern: inputstream → load → compile + JasperDesign jasperDesign = JRXmlLoader.load(inputStream); + JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign); + + log.info("Successfully compiled report: {}", reportName); + return jasperReport; + + } catch (Exception e) { + log.error("Failed to load/compile JRXML report {}: {}", jrxmlPath, e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("Could not load or compile report: " + reportName, e); + } + } + + /** + * Robust resource loading method that works in both development and JAR environments + */ + private static InputStream loadResourceAsStream(String resourcePath) { + // Try multiple approaches to load the resource + InputStream inputStream = null; + + // Method 1: Using current class classloader + try { + inputStream = MedicineInvoice.class.getResourceAsStream(resourcePath); + if (inputStream != null) { + log.info("Successfully loaded resource using class.getResourceAsStream: {}", resourcePath); + return inputStream; + } + } catch (Exception e) { + log.warn("Failed to load resource using class.getResourceAsStream: {}", e.getMessage()); + } + + // Method 2: Using thread context classloader (without leading slash) + try { + String pathWithoutSlash = resourcePath.startsWith("/") ? resourcePath.substring(1) : resourcePath; + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + inputStream = contextClassLoader.getResourceAsStream(pathWithoutSlash); + if (inputStream != null) { + log.info("Successfully loaded resource using context classloader: {}", pathWithoutSlash); + return inputStream; + } + } catch (Exception e) { + log.warn("Failed to load resource using context classloader: {}", e.getMessage()); + } + + // Method 3: Using system classloader + try { + String pathWithoutSlash = resourcePath.startsWith("/") ? resourcePath.substring(1) : resourcePath; + inputStream = ClassLoader.getSystemResourceAsStream(pathWithoutSlash); + if (inputStream != null) { + log.info("Successfully loaded resource using system classloader: {}", pathWithoutSlash); + return inputStream; + } + } catch (Exception e) { + log.warn("Failed to load resource using system classloader: {}", e.getMessage()); + } + + // Method 4: Try with different class as anchor + try { + inputStream = LocalStorage.class.getResourceAsStream(resourcePath); + if (inputStream != null) { + log.info("Successfully loaded resource using LocalStorage.class: {}", resourcePath); + return inputStream; + } + } catch (Exception e) { + log.warn("Failed to load resource using LocalStorage.class: {}", e.getMessage()); + } + + // Method 5: Try loading from external file system (fallback for JAR issues) + try { + String externalPath = resourcePath.startsWith("/") ? resourcePath.substring(1) : resourcePath; + java.io.File externalFile = new java.io.File(externalPath); + if (externalFile.exists()) { + inputStream = new FileInputStream(externalFile); + log.info("Successfully loaded resource from external file: {}", externalFile.getAbsolutePath()); + return inputStream; + } + } catch (Exception e) { + log.warn("Failed to load resource from external file: {}", e.getMessage()); + } + + // Method 6: Try loading from src/main/resources (development mode) + try { + String srcPath = "src/main/resources" + (resourcePath.startsWith("/") ? "" : "/") + resourcePath; + java.io.File srcFile = new java.io.File(srcPath); + if (srcFile.exists()) { + inputStream = new FileInputStream(srcFile); + log.info("Successfully loaded resource from source directory: {}", srcFile.getAbsolutePath()); + return inputStream; + } + } catch (Exception e) { + log.warn("Failed to load resource from source directory: {}", e.getMessage()); + } + + log.error("All resource loading methods failed for: {}", resourcePath); + return null; + } private JDialog dialog; private String patientName; @@ -94,16 +209,21 @@ public class MedicineInvoice{ /** * Shows the invoice directly in JasperViewer without creating a custom dialog */ - public void showDirectJasperViewer() { + public void showDirectJasperViewer() throws Exception { try { // This method now only fills the report and shows the viewer fillJasperPrint(); + + if (jasperPrint == null) { + throw new Exception("JasperPrint is null - report compilation failed"); + } + // Use the constructor to prevent the application from closing JasperViewer viewer = new JasperViewer(jasperPrint, false); viewer.setVisible(true); } catch (Exception e) { - e.printStackTrace(); - JOptionPane.showMessageDialog(null, "Error generating PDF: " + e.getMessage()); + log.info("error showing the jasper viewer, {}", e.getMessage()); + throw e; } } @@ -150,7 +270,7 @@ public class MedicineInvoice{ String pdfPath = "temp_medical_invoice.pdf"; // Use a temporary file name try { generatePdf(pdfPath); - displayPdfInLabel(pdfPath, pdfViewer); + } catch (Exception e) { e.printStackTrace(); JOptionPane.showMessageDialog(dialog, "Error generating PDF: " + e.getMessage()); @@ -209,6 +329,12 @@ public class MedicineInvoice{ try { // This method is almost identical to generatePdf, but exports to a byte array fillJasperPrint(); // Ensures jasperPrint is filled + + if (jasperPrint == null) { + throw new Exception("JasperPrint is null - report compilation failed"); + } + + log.info("Exporting JasperPrint to PDF bytes..."); return JasperExportManager.exportReportToPdf(jasperPrint); } catch (Exception e) { e.printStackTrace(); @@ -220,6 +346,11 @@ public class MedicineInvoice{ try { // Refactored to use a common method to fill the report fillJasperPrint(); + + if (jasperPrint == null) { + throw new Exception("JasperPrint is null - report compilation failed"); + } + // Export to PDF file JasperExportManager.exportReportToPdfFile(jasperPrint, pdfPath); } catch (Exception e) { @@ -254,7 +385,7 @@ public class MedicineInvoice{ // Call signature: (Tên, Ghi chú, Liều dùng, Đường dùng, Số lượng) medicineItems.add(InvoiceItem.createMedicine(medName, note, dosageInfo, route, amount)); } catch (Exception e) { - System.err.println("Error parsing medicine data row: " + java.util.Arrays.toString(medicine) + " | Error: " + e.getMessage()); + log.info("Error parsing medicine data row: " + java.util.Arrays.toString(medicine) + " | Error: " + e.getMessage()); } } } @@ -271,7 +402,7 @@ public class MedicineInvoice{ Double.parseDouble(service[3]) )); } catch (NumberFormatException e) { - System.err.println("Error parsing service data: " + e.getMessage()); + log.info("Error parsing service data: " + e.getMessage()); } } } @@ -293,7 +424,7 @@ public class MedicineInvoice{ supplementItems.add(InvoiceItem.createSupplement(supName, supNote, dosageInfo, supRoute, amount)); } catch (Exception e) { - System.err.println("Error parsing supplement data row: " + java.util.Arrays.toString(supplement) + " | Error: " + e.getMessage()); + log.info("Error parsing supplement data row: " + java.util.Arrays.toString(supplement) + " | Error: " + e.getMessage()); } } } @@ -308,7 +439,7 @@ public class MedicineInvoice{ parameters.put("medicineDS", medicineDS); parameters.put("serviceDS", serviceDS); parameters.put("supplementDS", supplementDS); - parameters.put(JRParameter.REPORT_LOCALE, new java.util.Locale("vi", "VN")); + parameters.put(JRParameter.REPORT_LOCALE, java.util.Locale.of("vi", "VN")); parameters.put("patientName", patientName != null ? patientName : ""); parameters.put("patientDOB", patientDOB != null ? patientDOB : ""); parameters.put("patientGender", patientGender != null ? patientGender : ""); @@ -321,93 +452,47 @@ public class MedicineInvoice{ parameters.put("checkupDate", date != null ? date : ""); parameters.put("patientDiagnos", diagnosis != null ? diagnosis : ""); parameters.put("checkupNote", notes != null ? notes : ""); - parameters.put("barcodeNumber", id != null ? id : ""); - parameters.put("driveURL", driveURL != null ? driveURL : ""); parameters.put("hasMedicines", !medicineItems.isEmpty()); parameters.put("hasServices", !serviceItems.isEmpty()); parameters.put("hasSupplements", !supplementItems.isEmpty()); + parameters.put("id", id); - String logoPath = "/BsK/client/ui/assets/icon/logo.jpg"; - // Load the logo as an InputStream - InputStream logoStream = MedicineInvoice.class.getResourceAsStream(logoPath); - if (logoStream == null) { - System.err.println("ERROR: Logo image not found on classpath: " + logoPath); - parameters.put("logoImage", null); - } else { - // Pass the InputStream to the report parameter - parameters.put("logoImage", logoStream); - } - String resourcePath = "/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml"; - try (InputStream inputStream = MedicineInvoice.class.getResourceAsStream(resourcePath)) { - if (inputStream == null) { - - throw new FileNotFoundException("Không thể tìm thấy tệp mẫu Jasper report trong classpath: " + resourcePath); - } - JasperDesign jasperDesign = JRXmlLoader.load(inputStream); - JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign); - jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource()); - } - } - - private void displayPdfInLabel(String pdfPath, JPanel pdfPanel) throws Exception { - PDDocument document = PDDocument.load(new File(pdfPath)); - PDFRenderer pdfRenderer = new PDFRenderer(document); - - pdfPanel.setLayout(new BoxLayout(pdfPanel, BoxLayout.Y_AXIS)); - - for (int i = 0; i < document.getNumberOfPages(); i++) { - BufferedImage image = pdfRenderer.renderImageWithDPI(i, 150); - ImageIcon icon = new ImageIcon(image); - JLabel pageLabel = new JLabel(icon); - pdfPanel.add(pageLabel); - } - document.close(); - - dialog.revalidate(); - dialog.repaint(); - } - - - public static void printPdf(File pdfFile) { - try (PDDocument document = PDDocument.load(pdfFile)) { - PrinterJob printerJob = PrinterJob.getPrinterJob(); - printerJob.setJobName("Print Medicine Invoice"); - - if (printerJob.printDialog()) { - printerJob.setPrintable((graphics, pageFormat, pageIndex) -> { - if (pageIndex >= document.getNumberOfPages()) { - return Printable.NO_SUCH_PAGE; - } - - Graphics2D g2 = (Graphics2D) graphics; - g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); - - try { - PDFRenderer pdfRenderer = new PDFRenderer(document); - BufferedImage pageImage = pdfRenderer.renderImageWithDPI(pageIndex, 300); - - double scale = Math.min(pageFormat.getImageableWidth() / pageImage.getWidth(), - pageFormat.getImageableHeight() / pageImage.getHeight()); - - g2.scale(scale, scale); - g2.drawImage(pageImage, 0, 0, null); - - } catch (IOException e) { - e.printStackTrace(); - return Printable.NO_SUCH_PAGE; - } - - return Printable.PAGE_EXISTS; - }); - - printerJob.print(); - System.out.println("Printing completed."); - } else { - System.out.println("Print job canceled."); - } + // FIXED: Generate barcode and QR code as BufferedImage instead of InputStream + try { + // Generate barcode and get file path + String barcodePath = BarcodeGenerator.generateCode128WithFixedName(id, 200, 50, "invoice_barcode"); + parameters.put("barcodeNumber", barcodePath); + + // Generate QR code for the driveURL + String qrcodePath = BarcodeGenerator.generateQRCodeWithFixedName(driveURL, 220, 220, "invoice_qrcode"); + parameters.put("driveURL", qrcodePath); + } catch (Exception e) { + log.error("Error generating barcode/QR code images: {}", e.getMessage()); + // Set fallback parameters as null or empty if image generation fails + parameters.put("barcodeNumber", null); + parameters.put("driveURL", null); + } + + try { + // Use the new method that tries pre-compiled .jasper files first + JasperReport jasperReport = loadCompiledReport("medserinvoice"); + log.info("Successfully loaded/compiled jasper report"); + + log.info("Attempting to fill report with parameters..."); + jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource()); + log.info("Successfully filled jasper print"); + + // Verify jasperPrint is not null and has content + if (jasperPrint != null) { + log.info("JasperPrint created successfully with {} pages", jasperPrint.getPages().size()); + } else { + log.error("JasperPrint is null after fill operation!"); + } + } + catch (Exception e) { + log.info("error loading the input stream, {}", e.getMessage()); e.printStackTrace(); - JOptionPane.showMessageDialog(null, "Error printing PDF: " + e.getMessage()); } } diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/UltrasoundResult.java b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/UltrasoundResult.java index f6fe863..34c17e5 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/UltrasoundResult.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/UltrasoundResult.java @@ -1,30 +1,10 @@ package BsK.client.ui.component.CheckUpPage.PrintDialog; import BsK.client.LocalStorage; -import com.itextpdf.io.font.constants.StandardFonts; -import com.itextpdf.io.image.ImageDataFactory; -import com.itextpdf.kernel.colors.ColorConstants; -import com.itextpdf.kernel.font.PdfFont; -import com.itextpdf.kernel.font.PdfFontFactory; -import com.itextpdf.kernel.geom.PageSize; -import com.itextpdf.kernel.pdf.PdfDocument; -import com.itextpdf.kernel.pdf.PdfWriter; -import com.itextpdf.layout.Document; -import com.itextpdf.layout.borders.Border; -import com.itextpdf.layout.borders.SolidBorder; -import com.itextpdf.layout.element.Cell; -import com.itextpdf.layout.element.Image; -import com.itextpdf.layout.element.Paragraph; -import com.itextpdf.layout.element.Table; -import com.itextpdf.layout.element.Text; -import com.itextpdf.layout.property.HorizontalAlignment; -import com.itextpdf.layout.property.TextAlignment; -import com.itextpdf.layout.property.UnitValue; -import com.itextpdf.layout.property.VerticalAlignment; -import com.itextpdf.layout.element.AreaBreak; + + import lombok.extern.slf4j.Slf4j; -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.rendering.PDFRenderer; + // --- JasperReports Imports --- import net.sf.jasperreports.engine.JREmptyDataSource; @@ -51,6 +31,7 @@ import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.io.*; +import java.io.ByteArrayInputStream; import java.util.List; import java.util.Map; import java.util.HashMap; @@ -58,10 +39,120 @@ import java.util.Locale; import net.sf.jasperreports.engine.JRParameter; import net.sf.jasperreports.engine.JRException; import java.util.ArrayList; -import org.slf4j.LoggerFactory; + @Slf4j public class UltrasoundResult { + + /** + * Load and compile Jasper report from JRXML with robust error handling for JAR environment + */ + private static JasperReport loadCompiledReport(String reportName) { + String jrxmlPath = "/print_forms/" + reportName + ".jrxml"; + + try (InputStream jrxmlStream = loadResourceAsStream(jrxmlPath)) { + if (jrxmlStream == null) { + throw new RuntimeException("JRXML resource not found: " + jrxmlPath); + } + + log.info("Compiling JRXML at runtime: {}", jrxmlPath); + + // Read all bytes to ensure complete stream reading in JAR environment + byte[] jrxmlContent = jrxmlStream.readAllBytes(); + log.info("JRXML content size: {} bytes", jrxmlContent.length); + + if (jrxmlContent.length == 0) { + throw new RuntimeException("JRXML file is empty: " + jrxmlPath); + } + + // Use ByteArrayInputStream for reliable reading in JAR environment + try (ByteArrayInputStream byteStream = new ByteArrayInputStream(jrxmlContent)) { + log.info("Loading JRXML design..."); + JasperDesign jasperDesign = JRXmlLoader.load(byteStream); + log.info("Compiling Jasper report..."); + JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign); + log.info("Successfully compiled report: {}", reportName); + return jasperReport; + } + } catch (Exception e) { + log.error("Failed to compile JRXML report {}: {}", jrxmlPath, e.getMessage()); + e.printStackTrace(); + throw new RuntimeException("Could not load or compile report: " + reportName, e); + } + } + + /** + * Robust resource loading method that works in both development and JAR environments + */ + private static InputStream loadResourceAsStream(String resourcePath) { + // Try multiple approaches to load the resource + InputStream inputStream = null; + + // Method 1: Using current class classloader + try { + inputStream = UltrasoundResult.class.getResourceAsStream(resourcePath); + if (inputStream != null) { + log.info("Successfully loaded resource using class.getResourceAsStream: {}", resourcePath); + return inputStream; + } + } catch (Exception e) { + log.warn("Failed to load resource using class.getResourceAsStream: {}", e.getMessage()); + } + + // Method 2: Using thread context classloader (without leading slash) + try { + String pathWithoutSlash = resourcePath.startsWith("/") ? resourcePath.substring(1) : resourcePath; + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + inputStream = contextClassLoader.getResourceAsStream(pathWithoutSlash); + if (inputStream != null) { + log.info("Successfully loaded resource using context classloader: {}", pathWithoutSlash); + return inputStream; + } + } catch (Exception e) { + log.warn("Failed to load resource using context classloader: {}", e.getMessage()); + } + + // Method 3: Using system classloader + try { + String pathWithoutSlash = resourcePath.startsWith("/") ? resourcePath.substring(1) : resourcePath; + inputStream = ClassLoader.getSystemResourceAsStream(pathWithoutSlash); + if (inputStream != null) { + log.info("Successfully loaded resource using system classloader: {}", pathWithoutSlash); + return inputStream; + } + } catch (Exception e) { + log.warn("Failed to load resource using system classloader: {}", e.getMessage()); + } + + // Method 5: Try loading from external file system (fallback for JAR issues) + try { + String externalPath = resourcePath.startsWith("/") ? resourcePath.substring(1) : resourcePath; + java.io.File externalFile = new java.io.File(externalPath); + if (externalFile.exists()) { + inputStream = new java.io.FileInputStream(externalFile); + log.info("Successfully loaded resource from external file: {}", externalFile.getAbsolutePath()); + return inputStream; + } + } catch (Exception e) { + log.warn("Failed to load resource from external file: {}", e.getMessage()); + } + + // Method 6: Try loading from src/main/resources (development mode) + try { + String srcPath = "src/main/resources" + (resourcePath.startsWith("/") ? "" : "/") + resourcePath; + java.io.File srcFile = new java.io.File(srcPath); + if (srcFile.exists()) { + inputStream = new java.io.FileInputStream(srcFile); + log.info("Successfully loaded resource from source directory: {}", srcFile.getAbsolutePath()); + return inputStream; + } + } catch (Exception e) { + log.warn("Failed to load resource from source directory: {}", e.getMessage()); + } + + log.error("All resource loading methods failed for: {}", resourcePath); + return null; + } private JDialog dialog; private final String checkupId; @@ -171,20 +262,20 @@ public class UltrasoundResult { } public JasperPrint createJasperPrint() throws JRException, IOException { - String jrxmlPath; + String reportName; if ("Dọc".equalsIgnoreCase(this.printType)) { - jrxmlPath = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml"; + reportName = "ultrasoundresult_potrait"; } else { - jrxmlPath = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml"; + reportName = "ultrasoundresult"; } - log.info("Using JRXML template: {}", jrxmlPath); + log.info("Using Jasper report template: {}", reportName); - try (InputStream inputStream = new FileInputStream(new File(jrxmlPath))) { + try { Map parameters = new HashMap<>(); // Set locale for date/number formatting - parameters.put(JRParameter.REPORT_LOCALE, new Locale("vi", "VN")); + parameters.put(JRParameter.REPORT_LOCALE, Locale.of("vi", "VN")); // Populate all the string parameters parameters.put("clinicPrefix", LocalStorage.ClinicPrefix != null ? LocalStorage.ClinicPrefix : ""); @@ -240,14 +331,16 @@ public class UltrasoundResult { // Google Drive URL for QR code generation parameters.put("driveURL", this.driveUrl); - // Load and compile the report - JasperDesign jasperDesign = JRXmlLoader.load(inputStream); - JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign); + // Load pre-compiled report or compile JRXML as fallback + JasperReport jasperReport = loadCompiledReport(reportName); // Fill the report JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource()); return jasperPrint; + } catch (Exception e) { + log.error("Error creating JasperPrint for report {}: {}", reportName, e.getMessage()); + throw new JRException("Failed to create ultrasound report", e); } } @@ -265,488 +358,4 @@ public class UltrasoundResult { return JasperExportManager.exportReportToPdf(jasperPrint); } - public void createDialog(JFrame parent) { - dialog = new JDialog(parent, "Kết quả siêu âm", true); - dialog.setSize(800, 700); - dialog.setResizable(true); - dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - dialog.setLayout(new BorderLayout()); - - dialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL); - - dialog.addWindowListener(new WindowAdapter() { - @Override - public void windowIconified(WindowEvent e) { - if (parent != null) parent.setState(Frame.ICONIFIED); - } - - @Override - public void windowDeiconified(WindowEvent e) { - if (parent != null && parent.getState() == Frame.ICONIFIED) parent.setState(Frame.NORMAL); - } - }); - - JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - JButton saveButton = new JButton("Lưu PDF"); - JButton printButton = new JButton("In PDF"); - buttonPanel.add(saveButton); - buttonPanel.add(printButton); - - JPanel pdfViewer = new JPanel(); - JScrollPane scrollPane = new JScrollPane(pdfViewer); - dialog.add(scrollPane, BorderLayout.CENTER); - dialog.add(buttonPanel, BorderLayout.SOUTH); - - try { - generatePdf(PDF_PATH); - displayPdfInLabel(PDF_PATH, pdfViewer); - } catch (Exception e) { - log.error("Error generating ultrasound PDF", e); - JOptionPane.showMessageDialog(dialog, "Lỗi khi tạo PDF: " + e.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE); - } - - saveButton.addActionListener(e -> savePdf()); - printButton.addActionListener(e -> printPdf()); - - dialog.setLocationRelativeTo(parent); - dialog.setVisible(true); - } - - private void generatePdf(String pdfPath) throws Exception { - PdfWriter writer = new PdfWriter(pdfPath); - PdfDocument pdfDoc = new PdfDocument(writer); - Document document; - - boolean isLandscape = "ngang".equalsIgnoreCase(this.printType); - if (isLandscape) { - document = new Document(pdfDoc, PageSize.A4.rotate()); - } else { - document = new Document(pdfDoc, PageSize.A4); - } - document.setMargins(20, 20, 20, 20); - - String boldFontPath = "src/main/java/BsK/client/ui/assets/font/SVN-Arial Bold.ttf"; - PdfFont boldFont = PdfFontFactory.createFont(boldFontPath, "Identity-H", true); - String fontPath = "src/main/java/BsK/client/ui/assets/font/SVN-Arial Regular.ttf"; - PdfFont font = PdfFontFactory.createFont(fontPath, "Identity-H", true); - - String title = templateTitle != null ? templateTitle.toUpperCase() : "KẾT QUẢ SIÊU ÂM"; - - if (isLandscape) { - generateLandscapeLayout(document, boldFont, font, title); - } else { - addHeader(document, boldFont, font); - document.add(new Paragraph(title) - .setFont(boldFont).setFontSize(14) - .setTextAlignment(TextAlignment.CENTER) - .setMarginBottom(5)); - generatePortraitLayout(document, boldFont, font); - } - - document.close(); - } - - private Cell createHeaderBlock(PdfFont boldFont, PdfFont font) { - Cell headerContainer = new Cell().setBorder(Border.NO_BORDER).setPadding(0); - Table headerTable = new Table(UnitValue.createPercentArray(new float[]{0.3f, 0.7f})) - .setWidth(UnitValue.createPercentValue(100)) - .setBorder(Border.NO_BORDER) - .setMarginBottom(2); - try { - String logoPath = "src/main/java/BsK/client/ui/assets/icon/clinic_logo.png"; - Image logo = new Image(ImageDataFactory.create(logoPath)).scaleToFit(50, 50); - headerTable.addCell(new Cell().add(logo).setBorder(Border.NO_BORDER).setVerticalAlignment(VerticalAlignment.MIDDLE)); - } catch (Exception e) { - headerTable.addCell(new Cell().setBorder(Border.NO_BORDER)); - log.warn("Clinic logo not found", e); - } - - Paragraph clinicInfo = new Paragraph() - .add(new Text(LocalStorage.ClinicPrefix + " " + LocalStorage.ClinicName + "\n").setFont(boldFont).setFontSize(10)) - .add(new Text("Địa chỉ: " + LocalStorage.ClinicAddress + "\n").setFontSize(8)) - .add(new Text("Điện thoại: " + LocalStorage.ClinicPhone).setFontSize(8)) - .setFont(font); - headerTable.addCell(new Cell().add(clinicInfo).setBorder(Border.NO_BORDER).setVerticalAlignment(VerticalAlignment.MIDDLE)); - - headerContainer.add(headerTable); - return headerContainer; - } - - private void generateLandscapeLayout(Document document, PdfFont boldFont, PdfFont font, String title) throws Exception { - Table titleTable = new Table(UnitValue.createPercentArray(new float[]{1, 1})) - .setWidth(UnitValue.createPercentValue(100)) - .setMarginBottom(2); - - Paragraph titleParagraph = new Paragraph(title) - .setFont(boldFont).setFontSize(14) - .setTextAlignment(TextAlignment.CENTER) - .setMarginBottom(2); - - Table mainTable = new Table(UnitValue.createPercentArray(new float[]{1.2f, 0.8f})) - .setWidth(UnitValue.createPercentValue(100)) - .setMarginTop(0); - - Cell leftCell = new Cell().setBorder(Border.NO_BORDER).setPadding(1).setVerticalAlignment(VerticalAlignment.TOP); - - leftCell.add(createHeaderBlock(boldFont, font)); - - leftCell.add(new Paragraph(title) - .setFont(boldFont).setFontSize(14) - .setTextAlignment(TextAlignment.CENTER) - .setMarginBottom(2)); - - addPatientInfo(leftCell, boldFont, font, 8); - leftCell.add(new Paragraph("NỘI DUNG").setFont(boldFont).setFontSize(10).setMarginTop(2) - .setTextAlignment(TextAlignment.LEFT).setUnderline()); - - Image rtfImage = createRtfImage(this.rtfContent, PageSize.A4.rotate().getWidth() * 0.58f); - if (rtfImage != null) { - // Set maximum height to prevent page overflow - ensure single page layout - rtfImage.setMaxHeight(320); // Fixed max height to ensure single page - rtfImage.setAutoScale(false); - leftCell.add(rtfImage); - } - - // Add conclusion and suggestion to left column - leftCell.add(new Paragraph("KẾT LUẬN").setFont(boldFont).setFontSize(10).setMarginTop(3) - .setTextAlignment(TextAlignment.LEFT).setUnderline()); - leftCell.add(new Paragraph(this.conclusion).setFont(font).setFontSize(8)); - - leftCell.add(new Paragraph("ĐỀ NGHỊ").setFont(boldFont).setFontSize(10).setMarginTop(2) - .setTextAlignment(TextAlignment.LEFT).setUnderline()); - leftCell.add(new Paragraph(this.suggestion).setFont(font).setFontSize(8)); - - Cell rightCell = new Cell().setBorder(Border.NO_BORDER).setPadding(1).setVerticalAlignment(VerticalAlignment.TOP); - rightCell.add(new Paragraph("HÌNH ẢNH SIÊU ÂM").setFont(boldFont).setFontSize(10) - .setTextAlignment(TextAlignment.LEFT).setUnderline().setMarginBottom(1)); - Table imageTable = createImageTable(selectedImages); - rightCell.add(imageTable); - - rightCell.add(createSignatureBlock(font, boldFont)); - - mainTable.addCell(leftCell); - mainTable.addCell(rightCell); - document.add(mainTable); - } - - private void generatePortraitLayout(Document document, PdfFont boldFont, PdfFont font) throws Exception { - boolean multiPage = selectedImages.size() > 4; - addPatientInfo(document, boldFont, font, 8); - - if (multiPage) { - Table contentTable = new Table(1).setWidth(UnitValue.createPercentValue(100)); - Cell contentCell = new Cell().setBorder(Border.NO_BORDER).setPadding(0); - - contentCell.add(new Paragraph("NỘI DUNG").setFont(boldFont).setFontSize(10).setMarginTop(5) - .setTextAlignment(TextAlignment.LEFT).setUnderline()); - Image rtfImage = createRtfImage(this.rtfContent, PageSize.A4.getWidth() - 60); - if (rtfImage != null) { - contentCell.add(rtfImage); - } - - contentCell.add(new Paragraph("KẾT LUẬN").setFont(boldFont).setFontSize(10).setMarginTop(5) - .setTextAlignment(TextAlignment.LEFT).setUnderline()); - contentCell.add(new Paragraph(this.conclusion).setFont(font).setFontSize(8)); - contentCell.add(new Paragraph("ĐỀ NGHỊ").setFont(boldFont).setFontSize(10).setMarginTop(5) - .setTextAlignment(TextAlignment.LEFT).setUnderline()); - contentCell.add(new Paragraph(this.suggestion).setFont(font).setFontSize(8)); - - contentTable.addCell(contentCell); - document.add(contentTable); - addSignature(document, font, boldFont); - - document.add(new AreaBreak()); - addHeader(document, boldFont, font); - document.add(new Paragraph("HÌNH ẢNH SIÊU ÂM (tt)").setFont(boldFont).setFontSize(10) - .setTextAlignment(TextAlignment.LEFT).setUnderline()); - Table imageTable = createImageTable(selectedImages); - document.add(imageTable); - } else { - Table mainTable = new Table(UnitValue.createPercentArray(new float[]{1.2f, 0.8f})) - .setWidth(UnitValue.createPercentValue(100)) - .setMarginTop(5); - - Cell leftCell = new Cell().setBorder(Border.NO_BORDER).setPaddingRight(5); - leftCell.add(new Paragraph("NỘI DUNG").setFont(boldFont).setFontSize(10) - .setTextAlignment(TextAlignment.LEFT).setUnderline()); - Image rtfImage = createRtfImage(this.rtfContent, PageSize.A4.getWidth() * 0.55f - 40); - if (rtfImage != null) { - leftCell.add(rtfImage); - } - - Cell rightCell = new Cell().setBorder(Border.NO_BORDER); - rightCell.add(new Paragraph("HÌNH ẢNH").setFont(boldFont).setFontSize(10) - .setTextAlignment(TextAlignment.LEFT).setUnderline()); - Table imageTable = createImageTable(selectedImages); - rightCell.add(imageTable); - - mainTable.addCell(leftCell); - mainTable.addCell(rightCell); - document.add(mainTable); - - document.add(new Paragraph("KẾT LUẬN").setFont(boldFont).setFontSize(10).setMarginTop(5) - .setTextAlignment(TextAlignment.LEFT).setUnderline()); - document.add(new Paragraph(this.conclusion).setFont(font).setFontSize(8)); - document.add(new Paragraph("ĐỀ NGHỊ").setFont(boldFont).setFontSize(10).setMarginTop(5) - .setTextAlignment(TextAlignment.LEFT).setUnderline()); - document.add(new Paragraph(this.suggestion).setFont(font).setFontSize(8)); - - addSignature(document, font, boldFont); - } - } - - private Table createImageTable(List images) throws IOException { - int numImages = images.size(); - int numCols = numImages == 1 ? 1 : 2; - - Table imageTable = new Table(numCols) - .useAllAvailableWidth() - .setMarginTop(1).setMarginBottom(1); - - for(File imgFile : images) { - Image img = new Image(ImageDataFactory.create(imgFile.getAbsolutePath())); - - img.setAutoScale(true); - img.setMaxWidth(110f); - img.setMaxHeight(85f); - - Cell imgCell = new Cell() - .add(img) - .setBorder(new SolidBorder(ColorConstants.GRAY, 0.5f)) - .setPadding(1) - .setHorizontalAlignment(HorizontalAlignment.CENTER) - .setVerticalAlignment(VerticalAlignment.MIDDLE); - imageTable.addCell(imgCell); - } - - int remainder = numImages % numCols; - if (remainder != 0 && numImages > 1) { - imageTable.addCell(new Cell().setBorder(Border.NO_BORDER)); - } - return imageTable; - } - - private Image createRtfImage(String rtf, float availableWidth) { - if (rtf == null || rtf.trim().isEmpty()) { - return null; - } - try { - JTextPane pane = new JTextPane(); - pane.setEditorKit(new RTFEditorKit()); - pane.setBackground(Color.WHITE); - pane.setOpaque(true); - - pane.read(new ByteArrayInputStream(rtf.getBytes("ISO-8859-1")), null); - - pane.setSize(new Dimension((int) availableWidth, Integer.MAX_VALUE)); - Dimension prefSize = pane.getPreferredSize(); - pane.setSize(prefSize); - - int renderDPI = 240; // Further reduced DPI to make text appear even smaller - float scale = renderDPI / 72f; - - int imgWidth = (int) (prefSize.width * scale); - int imgHeight = (int) (prefSize.height * scale); - - if (imgWidth <= 0 || imgHeight <= 0) { - log.warn("Invalid image dimensions for RTF rendering: {}x{}", imgWidth, imgHeight); - return null; - } - - BufferedImage img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB); - Graphics2D g2d = img.createGraphics(); - - g2d.scale(scale, scale); - - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - - pane.paint(g2d); - g2d.dispose(); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.write(img, "png", baos); - Image itextImage = new Image(ImageDataFactory.create(baos.toByteArray())); - - // Set width directly. The reduced DPI has already made the content smaller. - itextImage.setWidth(UnitValue.createPointValue(prefSize.width)); - - return itextImage; - - } catch (Exception e) { - log.error("Failed to convert RTF to Image", e); - return null; - } - } - - private void addHeader(Document document, PdfFont boldFont, PdfFont font) { - Table headerTable = new Table(UnitValue.createPercentArray(new float[]{1.2f, 3.8f, 2f})) - .setWidth(UnitValue.createPercentValue(100)) - .setBorder(Border.NO_BORDER) - .setMarginBottom(5); - try { - String logoPath = "src/main/java/BsK/client/ui/assets/icon/clinic_logo.png"; - Image logo = new Image(ImageDataFactory.create(logoPath)).scaleToFit(50, 50); - headerTable.addCell(new Cell().add(logo).setBorder(Border.NO_BORDER).setVerticalAlignment(VerticalAlignment.MIDDLE)); - } catch (Exception e) { - headerTable.addCell(new Cell().setBorder(Border.NO_BORDER)); - log.warn("Clinic logo not found", e); - } - - Paragraph clinicInfo = new Paragraph() - .add(new Text(LocalStorage.ClinicName + "\n").setFont(boldFont).setFontSize(10)) - .add(new Text("Địa chỉ: " + LocalStorage.ClinicAddress + "\n").setFontSize(8)) - .add(new Text("Điện thoại: " + LocalStorage.ClinicPhone).setFontSize(8)) - .setFont(font); - headerTable.addCell(new Cell().add(clinicInfo).setBorder(Border.NO_BORDER).setVerticalAlignment(VerticalAlignment.MIDDLE)); - - headerTable.addCell(new Cell().setBorder(Border.NO_BORDER)); - document.add(headerTable); - } - - private void addPatientInfo(Object container, PdfFont boldFont, PdfFont font, int fontSize) { - Table table = new Table(UnitValue.createPercentArray(new float[]{1,1})) - .setWidth(UnitValue.createPercentValue(100)) - .setFontSize(fontSize).setFont(font) - .setBorder(new SolidBorder(ColorConstants.GRAY, 0.5f)) - .setMarginBottom(1); // Reduced margin for compactness - - Cell left = new Cell().setBorder(Border.NO_BORDER).setPadding(1); - left.add(new Paragraph().add(new Text("Họ tên: ").setFont(boldFont)).add(patientName)); - left.add(new Paragraph().add(new Text("Năm sinh: ").setFont(boldFont)).add(patientDOB)); - left.add(new Paragraph().add(new Text("Giới tính: ").setFont(boldFont)).add(patientGender)); - - Cell right = new Cell().setBorder(Border.NO_BORDER).setPadding(1); - right.add(new Paragraph().add(new Text("Địa chỉ: ").setFont(boldFont)).add(patientAddress)); - right.add(new Paragraph().add(new Text("Ngày khám: ").setFont(boldFont)).add(checkupDate)); - right.add(new Paragraph().add(new Text("Mã khám: ").setFont(boldFont)).add(checkupId)); - - table.addCell(left); - table.addCell(right); - - if (container instanceof Document) { - ((Document) container).add(table); - } else if (container instanceof Cell) { - ((Cell) container).add(table); - } - } - - private void addSignature(Document document, PdfFont font, PdfFont boldFont) { - Table signatureTable = new Table(UnitValue.createPercentArray(new float[]{1, 1})) - .setWidth(UnitValue.createPercentValue(100)) - .setMarginTop(5); - - signatureTable.addCell(new Cell().setBorder(Border.NO_BORDER)); // empty cell for alignment - - Paragraph signParagraph = new Paragraph() - .add("Ngày " + checkupDate.substring(0,2) + " tháng " + checkupDate.substring(3,5) + " năm " + checkupDate.substring(6) + "\n") - .add(new Text("BÁC SĨ ĐIỀU TRỊ\n").setFont(boldFont)) - .add(new Text("(Ký và ghi rõ họ tên)\n\n").setFontSize(9).setItalic()) // Reduced newlines for signature space - .add(doctorName) - .setTextAlignment(TextAlignment.CENTER) - .setFont(font).setFontSize(10); - - signatureTable.addCell(new Cell().add(signParagraph).setBorder(Border.NO_BORDER)); - document.add(signatureTable); - } - - private void displayPdfInLabel(String pdfPath, JPanel pdfPanel) { - pdfPanel.removeAll(); - pdfPanel.setLayout(new BoxLayout(pdfPanel, BoxLayout.Y_AXIS)); - - try (PDDocument document = PDDocument.load(new File(pdfPath))) { - PDFRenderer pdfRenderer = new PDFRenderer(document); - float scale = 0.6f; - for (int i = 0; i < document.getNumberOfPages(); i++) { - BufferedImage image = pdfRenderer.renderImageWithDPI(i, 120); - int newWidth = (int) (image.getWidth() * scale); - int newHeight = (int) (image.getHeight() * scale); - ImageIcon icon = new ImageIcon(image.getScaledInstance(newWidth, newHeight, java.awt.Image.SCALE_SMOOTH)); - JLabel pageLabel = new JLabel(icon); - pageLabel.setAlignmentX(Component.CENTER_ALIGNMENT); - pageLabel.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0)); - pdfPanel.add(pageLabel); - } - } catch (IOException e) { - log.error("Error displaying PDF", e); - pdfPanel.add(new JLabel("Lỗi khi hiển thị PDF: " + e.getMessage())); - } - pdfPanel.revalidate(); - pdfPanel.repaint(); - } - - private void savePdf() { - JFileChooser fileChooser = new JFileChooser(); - fileChooser.setDialogTitle("Lưu file PDF"); - fileChooser.setSelectedFile(new File("ultrasound_" + checkupId + ".pdf")); - int userSelection = fileChooser.showSaveDialog(dialog); - if (userSelection == JFileChooser.APPROVE_OPTION) { - File fileToSave = fileChooser.getSelectedFile(); - try { - generatePdf(fileToSave.getAbsolutePath()); - JOptionPane.showMessageDialog(dialog, "PDF đã được lưu: " + fileToSave.getAbsolutePath()); - } catch (Exception ex) { - log.error("Error saving PDF", ex); - JOptionPane.showMessageDialog(dialog, "Lỗi khi lưu PDF: " + ex.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE); - } - } - } - - private void printPdf() { - File pdfFile = new File(PDF_PATH); - try (PDDocument document = PDDocument.load(pdfFile)) { - PrinterJob printerJob = PrinterJob.getPrinterJob(); - printerJob.setJobName("In kết quả siêu âm - " + patientName); - - if (printerJob.printDialog()) { - printerJob.setPrintable(new Printable() { - @Override - public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { - if (pageIndex >= document.getNumberOfPages()) { - return NO_SUCH_PAGE; - } - try { - PDFRenderer pdfRenderer = new PDFRenderer(document); - BufferedImage pageImage = pdfRenderer.renderImageWithDPI(pageIndex, 300); - - Graphics2D g2d = (Graphics2D) graphics; - g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); - - double scale = Math.min(pageFormat.getImageableWidth() / pageImage.getWidth(), pageFormat.getImageableHeight() / pageImage.getHeight()); - g2d.drawImage(pageImage, 0, 0, (int) (pageImage.getWidth() * scale), (int) (pageImage.getHeight() * scale), null); - - return PAGE_EXISTS; - } catch (IOException e) { - log.error("Error rendering page for printing", e); - throw new PrinterException(e.getMessage()); - } - } - }); - printerJob.print(); - } - } catch (Exception e) { - log.error("Error printing PDF", e); - JOptionPane.showMessageDialog(dialog, "Lỗi khi in PDF: " + e.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE); - } - } - - private Table createSignatureBlock(PdfFont font, PdfFont boldFont) { - Table signatureTable = new Table(UnitValue.createPercentArray(new float[]{1, 1})) - .setWidth(UnitValue.createPercentValue(100)) - .setMarginTop(2); // Reduced margin for compactness - - signatureTable.addCell(new Cell().setBorder(Border.NO_BORDER)); // empty cell for alignment - - Paragraph signParagraph = new Paragraph() - .add("Ngày " + checkupDate.substring(0,2) + " tháng " + checkupDate.substring(3,5) + " năm " + checkupDate.substring(6) + "\n") - .add(new Text("BÁC SĨ ĐIỀU TRỊ\n").setFont(boldFont)) - .add(new Text("(Ký và ghi rõ họ tên)\n\n").setFontSize(9).setItalic()) // Reduced newlines for signature space - .add(doctorName) - .setTextAlignment(TextAlignment.CENTER) - .setFont(font).setFontSize(10); - - signatureTable.addCell(new Cell().add(signParagraph).setBorder(Border.NO_BORDER)); - return signatureTable; - } } \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jasper b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jasper deleted file mode 100644 index c826a789a31c1f7ddc0aa8e64daa6fca1c50a6f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60690 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 Och parameters = new HashMap<>(); - - // Set locale to Vietnamese for number formatting and dates - parameters.put(JRParameter.REPORT_LOCALE, new Locale("vi", "VN")); - - // 2. Sample data parameters - parameters.put("clinicName", "BSK CLINIC"); - parameters.put("clinicAddress", "123 Main Street, City, Country"); - parameters.put("clinicPhone", "0123-456-789"); - - parameters.put("patientName", "Nguyễn Thị B"); - parameters.put("patientDOB", "20/07/1985"); - parameters.put("patientGender", "Nữ"); - parameters.put("patientAddress", "456 Đường DEF, Phường UVW, Quận 2, TP.HCM"); - parameters.put("patientDiagnos", "Theo dõi thai kỳ tuần 12"); - - parameters.put("doctorName", "BS. Trần Văn C"); - parameters.put("checkupDate", "22/12/2024"); - - // 3. Add the RTF content for the notes, with corrected font sizes. - // RTF font sizes are in "half-points", so \fs24 = 12pt. - String rtfContent = "{\\rtf1\\ansi\n" + - "{\\fonttbl\\f0\\fnil Monospaced;\\f1\\fnil Times New Roman;}\n" + - "{\\colortbl\\red0\\green0\\blue0;\\red51\\green51\\blue51;\\red0\\green0\\blue255;}\n" + - "\n" + - "\\li0\\ri0\\fi0\\f1\\fs28\\i0\\b\\ul0\\cf2 Test\\fs24\\cf1\\par\n" + // Heading at 14pt, main text at 12pt - "\\par\n" + - "dawdawd\\par\n" + - "ad\\par\n" + - " aw\\par\n" + - "daw da\\b0 daw dad aw\\ul d awd awd \\par\n" + - "\\ul0\\tab\\tab\\ul awdawd\\ul0\\par\n" + - "\\li0\\ri0\\fi0\\ul0\\par\n" + - "}"; - parameters.put("checkupNote", rtfContent); - parameters.put("checkupConclusion", "Thai nhi phát triển bình thường, dự kiến sinh trong 3 tháng tới."); // Normal string - parameters.put("checkupSuggestion", "Bổ sung sắt và canxi, tái khám sau 4 tuần."); // Normal string - - - // 4. Add the re-checkup date - parameters.put("reCheckupDate", "ngày 29 tháng 12 năm 2024"); - - // 5. Add barcode number - parameters.put("barcodeNumber", "2024001235"); - - // 6. Set the number of images to display - int numberOfImages = 4; - parameters.put("numberImage", numberOfImages); - - // 7. Add logo image path - String projectDir = System.getProperty("user.dir"); - String logoPath = projectDir + "/src/main/java/BsK/client/ui/assets/icon/logo.jpg"; - parameters.put("logoImage", logoPath); - - // 8. Add paths for the 6 ultrasound images - String imageDir = projectDir + "/img_db/8/"; - String[] imageFiles = { - "IMG_8_20250619_033320.jpg", - "IMG_8_20250619_033621.jpg", - "IMG_8_20250619_033622.jpg", - "IMG_8_20250619_033623.jpg", - "IMG_8_20250628_220921.jpg", - "IMG_9_20250619_034013.jpg" - }; - - for (int i = 0; i < 6; i++) { - if (i < numberOfImages) { - parameters.put("image" + (i + 1), imageDir + imageFiles[i]); - } else { - parameters.put("image" + (i + 1), null); // Pass null for unused image slots - } - } - - // 9. Load and compile the report - String jrxmlPath = projectDir + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml"; - InputStream inputStream = new FileInputStream(new File(jrxmlPath)); - - JasperDesign jasperDesign = JRXmlLoader.load(inputStream); - JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign); - - // 10. Fill the report. Since there's no table, we use an JREmptyDataSource. - JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource()); - - // 11. View the report - JasperViewer.viewReport(jasperPrint, false); // `false` to prevent app exit on close - - } catch (Exception e) { - e.printStackTrace(); - } - } -} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jasper b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jasper deleted file mode 100644 index 85a7db4825c4ffdd7d59c382cf683719187b7346..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43444 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 CAST(strftime('%s', date('now', 'localtime', 'start of day')) AS INTEGER) * 1000 - AND - a.checkup_date < CAST(strftime('%s', date('now', 'localtime', 'start of day', '+1 day')) AS INTEGER) * 1000""" + date(a.checkup_date / 1000, 'unixepoch', '+7 hours') = date('now', '+7 hours')""" ); if (!rs.isBeforeFirst()) { @@ -2196,15 +2194,22 @@ public class ServerHandler extends SimpleChannelInboundHandler= CAST(strftime('%s', date('now', 'localtime', 'start of day')) AS INTEGER) * 1000\n" + - "AND a.checkup_date < CAST(strftime('%s', date('now', 'localtime', 'start of day', '+1 day')) AS INTEGER) * 1000" + """ + SELECT + a.checkup_id, a.checkup_date, c.customer_last_name, c.customer_first_name, + d.doctor_first_name, d.doctor_last_name, a.suggestion, a.diagnosis, a.notes, + a.status, a.customer_id, c.customer_number, c.customer_address, + a.customer_weight, a.customer_height, c.customer_gender, c.customer_dob, + a.checkup_type, a.conclusion, a.reCheckupDate, c.cccd_ddcn, a.heart_beat, + a.blood_pressure, c.drive_url, a.doctor_ultrasound_id, a.queue_number + FROM + checkup AS a + JOIN + customer AS c ON a.customer_id = c.customer_id + JOIN + Doctor D ON a.doctor_id = D.doctor_id + WHERE + date(a.checkup_date / 1000, 'unixepoch', '+7 hours') = date('now', '+7 hours')""" ); String[][] resultArray; diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml b/src/main/resources/print_forms/medserinvoice.jrxml similarity index 95% rename from src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml rename to src/main/resources/print_forms/medserinvoice.jrxml index b08e504..c95111c 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml +++ b/src/main/resources/print_forms/medserinvoice.jrxml @@ -35,7 +35,7 @@ - + @@ -61,7 +61,6 @@ - @@ -69,6 +68,7 @@ + @@ -563,45 +563,41 @@ - <element kind="image" uuid="c596ac6f-18d9-4cfe-a74e-58b1f7d11ecc" x="-10" y="-4" width="90" height="83"> - <expression><![CDATA[$P{logoImage}]]></expression> - </element> - <element kind="textField" uuid="48bf4793-8d8d-427d-b739-8f34f051192f" x="80" y="-4" width="359" height="25" forecolor="#FF0905" fontName="Times New Roman" fontSize="18.0" bold="true" hTextAlign="Center"> + <element kind="textField" uuid="48bf4793-8d8d-427d-b739-8f34f051192f" x="1" y="-4" width="359" height="25" forecolor="#FF0905" fontName="Times New Roman" fontSize="18.0" bold="true" hTextAlign="Left"> <expression><![CDATA[$P{clinicPrefix}]]></expression> </element> - <element kind="staticText" uuid="d8e95ce7-3ac0-44a7-ac8f-65821b7f373c" x="80" y="47" width="150" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left"> + <element kind="staticText" uuid="d8e95ce7-3ac0-44a7-ac8f-65821b7f373c" x="1" y="47" width="150" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left"> <text><![CDATA[Địa chỉ: ]]></text> </element> - <element kind="textField" uuid="9883203e-fd4b-4110-a4b9-2c8c257ce5fa" x="126" y="47" width="314" height="15" fontName="Times New Roman" fontSize="12.0"> + <element kind="textField" uuid="9883203e-fd4b-4110-a4b9-2c8c257ce5fa" x="47" y="47" width="314" height="15" fontName="Times New Roman" fontSize="12.0" hTextAlign="Left"> <expression><![CDATA[$P{clinicAddress}]]></expression> </element> - <element kind="staticText" uuid="f00706ca-903c-4089-b3a1-33be68f6112b" x="80" y="62" width="150" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left"> + <element kind="staticText" uuid="f00706ca-903c-4089-b3a1-33be68f6112b" x="1" y="62" width="150" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left"> <text><![CDATA[SĐT:]]></text> </element> - <element kind="textField" uuid="2af00e22-702e-42cb-a6fb-19d401a58170" x="110" y="62" width="330" height="15" fontName="Times New Roman" fontSize="12.0"> + <element kind="textField" uuid="2af00e22-702e-42cb-a6fb-19d401a58170" x="31" y="62" width="330" height="15" fontName="Times New Roman" fontSize="12.0" hTextAlign="Left"> <expression><![CDATA[$P{clinicPhone}]]></expression> </element> - <element kind="component" uuid="35037abf-0984-4b9e-8a05-f8fae38d7dcc" x="490" y="0" width="65" height="43"> - <component kind="barcode4j:Code128"> - <codeExpression><![CDATA[$P{barcodeNumber}]]></codeExpression> - </component> - </element> <element kind="staticText" uuid="de38fb46-49af-4f48-8c33-f97680bcc7fa" x="135" y="71" width="284" height="21" fontName="Times New Roman" fontSize="18.0" bold="true" hTextAlign="Center"> <text><![CDATA[TOA THUỐC & DỊCH VỤ]]></text> <property name="com.jaspersoft.studio.unit.height" value="pixel"/> </element> - <element kind="component" uuid="b8dc3582-162b-47d9-9501-fbf3bfc63fbd" x="440" y="0" width="46" height="39"> - <component kind="barcode4j:QRCode"> - <codeExpression><![CDATA[$P{driveURL}]]></codeExpression> - </component> - </element> - <element kind="staticText" uuid="53b28743-6b3c-4700-b84d-a96a93371dbf" x="434" y="43" width="55" height="14" fontName="Times New Roman" fontSize="8.0" bold="false" hTextAlign="Center"> - <printWhenExpression><![CDATA[$P{driveURL} != null && !$P{driveURL}.trim().isEmpty()]]></printWhenExpression> + <element kind="staticText" uuid="53b28743-6b3c-4700-b84d-a96a93371dbf" x="377" y="62" width="55" height="14" fontName="Times New Roman" fontSize="8.0" bold="false" hTextAlign="Center"> + <printWhenExpression><![CDATA[$P{driveURL} != null]]></printWhenExpression> <text><![CDATA[Xem KQ online]]></text> </element> - <element kind="textField" uuid="9dcab6b1-a9ce-4aa9-9df9-232869b3ab24" x="80" y="21" width="359" height="25" forecolor="#FF0905" fontName="Times New Roman" fontSize="18.0" bold="true" hTextAlign="Center"> + <element kind="textField" uuid="9dcab6b1-a9ce-4aa9-9df9-232869b3ab24" x="1" y="21" width="359" height="25" forecolor="#FF0905" fontName="Times New Roman" fontSize="18.0" bold="true" hTextAlign="Left"> <expression><![CDATA[$P{clinicName}]]></expression> </element> + <element kind="image" uuid="bd44c818-46a4-4ccf-8ac4-1e78cce1442c" x="370" y="-4" width="68" height="66"> + <expression><![CDATA[$P{driveURL}]]></expression> + </element> + <element kind="image" uuid="9a508076-24a7-46db-a8d4-e5e643f26bd2" x="451" y="-4" width="104" height="44"> + <expression><![CDATA[$P{barcodeNumber}]]></expression> + </element> + <element kind="textField" uuid="6fa6eee4-7f0a-4741-8ede-1fd222ffed22" x="451" y="41" width="104" height="15" fontName="Times New Roman" fontSize="12.0" hTextAlign="Center"> + <expression><![CDATA[$P{id}]]></expression> + </element> diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml b/src/main/resources/print_forms/ultrasoundresult.jrxml similarity index 100% rename from src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml rename to src/main/resources/print_forms/ultrasoundresult.jrxml diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml b/src/main/resources/print_forms/ultrasoundresult_potrait.jrxml similarity index 100% rename from src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml rename to src/main/resources/print_forms/ultrasoundresult_potrait.jrxml diff --git a/temp-jar-extract/META-INF/MANIFEST.MF b/temp-jar-extract/META-INF/MANIFEST.MF new file mode 100644 index 0000000..c76b615 --- /dev/null +++ b/temp-jar-extract/META-INF/MANIFEST.MF @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +Created-By: Maven Archiver 3.6.1 +Build-Jdk-Spec: 21 +Main-Class: BsK.client.Client + diff --git a/temp/barcodes/invoice_barcode.png b/temp/barcodes/invoice_barcode.png new file mode 100644 index 0000000000000000000000000000000000000000..5bcacfa4ab0163c904fc80d617979febc67012b4 GIT binary patch literal 103 zcmeAS@N?(olHy`uVBq!ia0vp^CxF<95lAo?YOVAEQhJ^)jv*Cs$v^)8|8HOR^WXn7 x^86>v|NS^z@u0r>Xmiy9jl(O>Kw!sYMur9I?pOY9wKM{m;OXk;vd$@?2>`;NCx`$5 literal 0 HcmV?d00001 diff --git a/temp/qrcodes/invoice_qrcode.png b/temp/qrcodes/invoice_qrcode.png new file mode 100644 index 0000000000000000000000000000000000000000..2053e46bc0858c6f22ed3c0a65e0374eb935fabd GIT binary patch literal 519 zcmV+i0{H!jP)RU0005bNklMn0Hvyl$;&YS!@A> zVM0oyb)tofvOkEefY`ZdvnKW?boGOt3K)9QnheFxJ_*Jkg`0~0Y$ve{F(Hf*c3>^y zk3++)fJhHBIDsWbC@N}S3Gv`cGCWgewl0O+rH!FoPyCE}7{%TYj}