mirror of
https://github.com/tiennm99/BSK-All-in-One-Clinic-Management-System.git
synced 2026-10-11 03:12:53 +00:00
[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.
This commit is contained in:
1 parent
e2bf7b6c62
commit
bb1ee74238
19 files changed
+777
-787
No files matched your search
Generated
+1
-1
@@ -8,5 +8,5 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="corretto-21" project-jdk-type="JavaSDK" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_24" default="true" project-jdk-name="corretto-21" project-jdk-type="JavaSDK" />
|
||||
</project>
|
||||
Generated
+6
-5
@@ -1,10 +1,11 @@
|
||||
<configuration name="BSK Server" type="Application" factoryName="Application" singleton="true">
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="BSK Server" type="Application" factoryName="Application" singleton="true">
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="corretto-22" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
|
||||
<option name="MAIN_CLASS_NAME" value="BsK.server.Server" />
|
||||
<module name="BSK-All-in-One-Clinic-Management-System" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="ALLOW_RUNNING_IN_PARALLEL" value="false" />
|
||||
<option name="SINGLE_INSTANCE_ONLY" value="true" />
|
||||
<module name="ldS_BsK" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
Binary file not shown.
@@ -138,17 +138,6 @@
|
||||
<artifactId>swingx</artifactId>
|
||||
<version>1.6.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.pdfbox</groupId>
|
||||
<artifactId>pdfbox</artifactId>
|
||||
<version>2.0.27</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itext7-core</artifactId>
|
||||
<version>7.1.12</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.sarxos</groupId>
|
||||
<artifactId>webcam-capture</artifactId>
|
||||
|
||||
@@ -1819,28 +1819,33 @@ public class CheckUpPage extends JPanel {
|
||||
supplements.toArray(new String[0][]) // Pass supplements
|
||||
);
|
||||
// First, show the print preview to the user
|
||||
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";
|
||||
// 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);
|
||||
// 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);
|
||||
}
|
||||
}).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;
|
||||
});
|
||||
break;
|
||||
case "ultrasound": // This case now handles "Lưu & In"
|
||||
// Step 1: Validate conditions first.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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.
|
||||
+171
-86
@@ -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,9 +29,125 @@ 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;
|
||||
private String patientDOB;
|
||||
@@ -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);
|
||||
// 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);
|
||||
}
|
||||
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);
|
||||
// Use the new method that tries pre-compiled .jasper files first
|
||||
JasperReport jasperReport = loadCompiledReport("medserinvoice");
|
||||
log.info("Successfully loaded/compiled jasper report");
|
||||
|
||||
double scale = Math.min(pageFormat.getImageableWidth() / pageImage.getWidth(),
|
||||
pageFormat.getImageableHeight() / pageImage.getHeight());
|
||||
log.info("Attempting to fill report with parameters...");
|
||||
jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource());
|
||||
log.info("Successfully filled jasper print");
|
||||
|
||||
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.");
|
||||
// Verify jasperPrint is not null and has content
|
||||
if (jasperPrint != null) {
|
||||
log.info("JasperPrint created successfully with {} pages", jasperPrint.getPages().size());
|
||||
} else {
|
||||
System.out.println("Print job canceled.");
|
||||
log.error("JasperPrint is null after fill operation!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.info("error loading the input stream, {}", e.getMessage());
|
||||
e.printStackTrace();
|
||||
JOptionPane.showMessageDialog(null, "Error printing PDF: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+126
-517
@@ -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,11 +39,121 @@ 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;
|
||||
private final String patientName;
|
||||
@@ -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<String, Object> 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<File> 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;
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
-115
@@ -1,115 +0,0 @@
|
||||
package BsK.client.ui.component.CheckUpPage.PrintDialog.print_forms;
|
||||
|
||||
import net.sf.jasperreports.engine.JREmptyDataSource;
|
||||
import net.sf.jasperreports.engine.JasperReport;
|
||||
import net.sf.jasperreports.engine.xml.JRXmlLoader;
|
||||
import net.sf.jasperreports.engine.design.JasperDesign;
|
||||
import net.sf.jasperreports.engine.JasperCompileManager;
|
||||
import net.sf.jasperreports.engine.JasperFillManager;
|
||||
import net.sf.jasperreports.engine.JasperPrint;
|
||||
import net.sf.jasperreports.view.JasperViewer;
|
||||
import net.sf.jasperreports.engine.JRParameter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class test_ultrasound_print {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// 1. Put the data sources into the parameters map
|
||||
Map<String, Object> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
@@ -124,9 +124,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
JOIN
|
||||
Doctor D ON a.doctor_id = D.doctor_id
|
||||
WHERE
|
||||
a.checkup_date >= 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<TextWebSocketFram
|
||||
try {
|
||||
// --- EXISTING LOGIC FOR THE QUEUE (UNCHANGED) ---
|
||||
ResultSet rs = statement.executeQuery(
|
||||
"select a.checkup_id, a.checkup_date, c.customer_last_name, c.customer_first_name,\n" +
|
||||
"d.doctor_first_name, d.doctor_last_name, a.suggestion, a.diagnosis, a.notes, a.status, a.customer_id, \n" +
|
||||
"c.customer_number, c.customer_address, a.customer_weight, a.customer_height, c.customer_gender, c.customer_dob, \n" +
|
||||
"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\n" +
|
||||
"from checkup as a\n" +
|
||||
"join customer as c on a.customer_id = c.customer_id\n" +
|
||||
"join Doctor D on a.doctor_id = D.doctor_id\n" +
|
||||
"where a.checkup_date >= 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;
|
||||
|
||||
+19
-23
@@ -35,7 +35,7 @@
|
||||
<expression><![CDATA[$V{medRowTotal}]]></expression>
|
||||
</variable>
|
||||
<variable name="serRowTotal" class="java.math.BigDecimal">
|
||||
<expression><![CDATA[new java.math.BigDecimal($F{serUnitPrice}).multiply(new java.math.BigDecimal($F{serAmount}))]]></expression>
|
||||
<expression><![CDATA[($F{serUnitPrice} == null || $F{serAmount} == null) ? new java.math.BigDecimal(0) : new java.math.BigDecimal($F{serUnitPrice}).multiply(new java.math.BigDecimal($F{serAmount}))]]></expression>
|
||||
</variable>
|
||||
<variable name="serTotal" calculation="Sum" class="java.math.BigDecimal">
|
||||
<expression><![CDATA[$V{serRowTotal}]]></expression>
|
||||
@@ -61,7 +61,6 @@
|
||||
<parameter name="patientName" class="java.lang.String"/>
|
||||
<parameter name="patientDOB" class="java.lang.String"/>
|
||||
<parameter name="clinicAddress" class="java.lang.String"/>
|
||||
<parameter name="logoImage" class="java.io.InputStream"/>
|
||||
<parameter name="barcodeNumber" class="java.lang.String"/>
|
||||
<parameter name="patientGender" class="java.lang.String"/>
|
||||
<parameter name="hasMedicines" class="java.lang.Boolean"/>
|
||||
@@ -69,6 +68,7 @@
|
||||
<parameter name="hasSupplements" class="java.lang.Boolean"/>
|
||||
<parameter name="driveURL" class="java.lang.String"/>
|
||||
<parameter name="clinicPrefix" class="java.lang.String"/>
|
||||
<parameter name="id" class="java.lang.String"/>
|
||||
<query language="sql"><![CDATA[]]></query>
|
||||
<variable name="MedicineGrandTotal" calculation="System" class="java.math.BigDecimal">
|
||||
<initialValueExpression><![CDATA[new java.math.BigDecimal(0)]]></initialValueExpression>
|
||||
@@ -563,45 +563,41 @@
|
||||
</group>
|
||||
<background splitType="Stretch"/>
|
||||
<title height="96" splitType="Stretch">
|
||||
<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>
|
||||
</title>
|
||||
<pageHeader height="72" splitType="Stretch">
|
||||
<element kind="staticText" uuid="e6997cbd-6cea-4ab2-ab14-402c6487566b" x="0" y="4" width="59" height="14" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,5 @@
|
||||
Manifest-Version: 1.0
|
||||
Created-By: Maven Archiver 3.6.1
|
||||
Build-Jdk-Spec: 21
|
||||
Main-Class: BsK.client.Client
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 103 B |
Binary file not shown.
|
After Width: | Height: | Size: 519 B |
Reference in new issue
Block a user