package org.example.Office;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// ====================== 1. Java原生基础类（IO、AWT、NIO等）======================

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
// ====================== 2. Spire.Doc相关（仅用于Word转HTML：SaveToHtml方法）======================
import com.spire.doc.Document;
import com.spire.doc.FileFormat;
import com.spire.doc.Section;
import com.spire.doc.PageSetup;
import com.spire.doc.documents.ImageType;
import com.spire.doc.documents.MarginsF;

// ====================== 3. Spire.XLS相关（仅用于Excel转HTML：SaveExcelToHtml方法）======================
import com.spire.xls.Workbook;
import com.spire.xls.Worksheet;
// 注：SaveExcelToHtml中用了完整路径com.spire.xls.PageSetup，此处不重复导入

// ====================== 4. iText 7相关（仅用于图片转PDF：iTextSharpCreatPDF方法）======================
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.geom.PageSize;
// iText7 核心布局类（解决 Image 和 Document 冲突）
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.properties.AreaBreakType;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.example.Utils.FormatFactoryUtil;

import static org.example.Utils.NativeExtensionUtils.ToInt64;
// 注：方法中用了完整路径com.itextpdf.layout.Document和com.itextpdf.layout.element.Image，此处不重复导入

public class OfficeUtil {
    private static final Logger log = LoggerFactory.getLogger(OfficeUtil.class);


    private static final String ImgHtml = "<!DOCTYPE html>\n" +
            "<html lang='en'>\n" +
            "<head>\n" +
            "    <meta http-equiv='X-UA-Compatible' content='IE=8,IE=9,IE=10,IE=11,IE=edge'>\n" +
            "    <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>\n" +
            "    <meta name='google' content='notranslate' />\n" +
            "    <style type='text/css'>*{{max-width:100%;}}img{{margin:auto;display:block;}}</style>\n" +
            "    <title>${0}</title>\n" + // 占位符 ${0}：页面标题
            "</head>\n" +
            "<body>\n" +
            "    ${3}\n" + // 占位符 ${3}：body 前置内容（如额外样式）
            "    <div class='body' style='${2}'>${1}</div>\n" + // ${2}：div样式，${1}：div内容
            "</body>\n" +
            "</html>";


    // 1. base64ToPng（无改动）
    public static void base64ToPng(String base64, String path) {
        byte[] arr = Base64.getDecoder().decode(base64);

        try (ByteArrayInputStream ms = new ByteArrayInputStream(arr)) {
            BufferedImage bmp = ImageIO.read(ms);
            File file = new File(path);

            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }

            if (!file.exists()) {
                ImageIO.write(bmp, "png", file);
            }
        } catch (IOException e) {
            log.error("Exception caught", e);
        }
    }


    // 2. TifToBase64（无改动）
    public static String TifToBase64(String tifPath) {
        try (ByteArrayOutputStream memoryStream = new ByteArrayOutputStream()) {
            File tifFile = new File(tifPath);
            BufferedImage image = ImageIO.read(tifFile);

            ImageIO.write(image, "png", memoryStream);
            memoryStream.flush();

            byte[] imageBytes = memoryStream.toByteArray();
            return Base64.getEncoder().encodeToString(imageBytes);
        } catch (IOException e) {
            log.error("Exception caught", e);
            return null;
        }
    }


    // 3. iTextSharpCreatPDF（修复所有iText 7错误）
    public static void iTextSharpCreatPDF(String imagepath, String pdfpath) throws MalformedURLException {
        // 修复1：ImageDataFactory.create可能抛MalformedURLException，声明抛出
        ImageData imageData = ImageDataFactory.create(imagepath);
        float percentage = 1f;
        float resizedWidth = imageData.getWidth();
        float resizedHeight = imageData.getHeight();

        // 修复2：用PageSize替代Rectangle（Document构造函数要求PageSize类型）
        PageSize pageSize = new PageSize(1000, 1000);

        try (PdfWriter writer = new PdfWriter(pdfpath);
             PdfDocument pdfDoc = new PdfDocument(writer);
             com.itextpdf.layout.Document doc = new com.itextpdf.layout.Document(pdfDoc, pageSize)) {

            doc.setMargins(0, 0, 0, 0);
            // 修复5：getPageEffectiveArea参数为PageSize，方法正常调用
            while (resizedWidth > doc.getPageEffectiveArea(pageSize).getWidth()) {
                percentage *= 0.9f;
                resizedHeight = imageData.getHeight() * percentage;
                resizedWidth = imageData.getWidth() * percentage;
            }

            com.itextpdf.layout.element.Image image = new com.itextpdf.layout.element.Image(imageData);
            image.scale(percentage, percentage);
            float centerX = (pageSize.getWidth() - resizedWidth) / 2;
            float centerY = (pageSize.getHeight() - resizedHeight) / 2;
            image.setFixedPosition(centerX, centerY);

            doc.add(image);

        } catch (IOException ioex) {
            log.error("Exception caught", ioex);
        } catch (Exception ex) {
            log.error("Exception caught", ex);
        }
    }


    // 4. SaveToHtml（无改动）
    public static void SaveToHtml(String urlpath, String hpath, String disCopy) {
        Document document = new Document();
        try {
            document.loadFromFile(urlpath);
            document.saveToFile(hpath, FileFormat.Html);

            Section section = document.getSections().get(0);
            PageSetup pageSetup = section.getPageSetup();
            double width = pageSetup.getPageSize().getWidth();
            MarginsF margins = pageSetup.getMargins();

            String style = String.format("<body style='width: %fpt;user-select:%s;margin: auto;border: 1px solid #ececec;padding:%fpt %fpt %fpt %fpt ;box-sizing: border-box;box-shadow: 0 0 15px 2px rgba(0,0,0,0.1);' >",
                    width,
                    disCopy,
                    margins.getTop(),
                    margins.getRight(),
                    margins.getBottom(),
                    margins.getLeft());

            File urlFile = new File(urlpath);
            if (urlFile.exists()) {
                String strContent = Files.readString(Paths.get(hpath), StandardCharsets.UTF_8);

                Pattern bodyPattern = Pattern.compile("(<body[^>]*?>)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
                strContent = bodyPattern.matcher(strContent).replaceFirst(style);

                strContent = strContent.replace("</head>", "<style type='text/css'>.Section0{overflow-x: visible;}</style></head>");

                Pattern charsetPattern = Pattern.compile("<meta[^<|>]+charset=+", Pattern.CASE_INSENSITIVE);
                if (!charsetPattern.matcher(strContent).find()) {
                    strContent = strContent.replace("<head>", "<head><meta http-equiv=Content-Type content='text/html; charset=utf-8'>");
                }

                Pattern lineHeightPattern = Pattern.compile("<p[^<|>]+line-height:0\\.+", Pattern.CASE_INSENSITIVE);
                if (lineHeightPattern.matcher(strContent).find()) {
                    strContent = strContent.replace("</head>", "<style type='text/css'>p{line-height:unset !important;}</style></head>");
                }

                Files.write(Paths.get(hpath), strContent.getBytes(StandardCharsets.UTF_8));
            }
        } catch (Exception e) {
            log.error("Exception caught", e);
            throw new RuntimeException(e);
        } finally {
            if (document != null) {
                document.dispose();
            }
        }
    }


    // 5. SaveExcelToHtml（无改动）
    public static void SaveExcelToHtml(String urlpath, String hpath, String htmlPath, String discopy) {
        String style = "";
        double width = 0;
        Workbook book = new Workbook();

        try {
            book.loadFromFile(urlpath);
            String saveHtmlPath = hpath;

            for (int i = 0; i < book.getWorksheets().getCount(); i++) {
                Worksheet worksheet = book.getWorksheets().get(i);

                if (i == 0) {
                    worksheet.saveToHtml(hpath);
                } else {
                    saveHtmlPath = hpath.replace(".html", "_" + i + ".html");
                    worksheet.saveToHtml(saveHtmlPath);
                }

                com.spire.xls.PageSetup pageSetup = worksheet.getPageSetup();
                width = book.getWorksheets().get(0).getPageSetup().getPageWidth();

                style = String.format("<body style='width: %fpt;user-select:%s;margin: auto;border: 1px solid #ececec;padding:%fpt %fpt %fpt %fpt ;box-sizing: border-box;box-shadow: 0 0 15px 2px rgba(0,0,0,0.1);' ><style type='text/css'>td{{border:1px solid black;}}</style>",
                        width,
                        discopy,
                        pageSetup.getTopMargin(),
                        pageSetup.getRightMargin(),
                        pageSetup.getBottomMargin(),
                        pageSetup.getLeftMargin());

                if (i <= book.getWorksheets().getCount() - 2) {
                    String nextPageUrl = htmlPath.replace(".html", "_" + (i + 1) + ".html");
                    style += "<a href='" + nextPageUrl + "'>下一页</a>";
                }

                if (i > 0) {
                    String prevPageUrl = (i == 1) ? htmlPath : htmlPath.replace(".html", "_" + (i - 1) + ".html");
                    style += "<a href='" + prevPageUrl + "'>上一页</a>";
                }

                File urlFile = new File(urlpath);
                if (urlFile.exists()) {
                    String strContent = Files.readString(Paths.get(saveHtmlPath), StandardCharsets.UTF_8);

                    String replaceStr = "background-color:rgb(0,0,0)";
                    if (strContent.indexOf(replaceStr) > 0) {
                        strContent = strContent.replace(replaceStr, "background-color:rgb(255,255,255)");
                    }

                    Pattern charsetPattern = Pattern.compile("<meta[^<|>]+charset=+", Pattern.CASE_INSENSITIVE);
                    if (!charsetPattern.matcher(strContent).find()) {
                        strContent = strContent.replace("<head>", "<head><meta http-equiv=Content-Type content='text/html; charset=utf-8'>");
                    }

                    Pattern bodyPattern = Pattern.compile("(<body[^>]*?>)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
                    strContent = bodyPattern.matcher(strContent).replaceFirst(style);

                    Files.write(Paths.get(saveHtmlPath), strContent.getBytes(StandardCharsets.UTF_8));
                }
            }
        } catch (Exception e) {
            log.error("Exception caught", e);
            throw new RuntimeException(e);
        } finally {
            if (book != null) {
                book.dispose();
            }
        }
    }

    public static void SaveToImg(String urlpath, String hpath, String extension) {
        SaveToImg(urlpath, hpath, extension, false); // 重载，适配 C# 默认参数
    }

    /**
     * 对齐 C# 逻辑：Word/Excel 转图片 + 生成 HTML，支持可选转 PDF
     *
     * @param urlpath   源文件路径（doc/docx/xls/xlsx）
     * @param hpath     生成的 HTML 路径
     * @param extension 文件扩展名（小写）
     * @param toPdf     是否同时生成 PDF（默认 false）
     */
    public static void SaveToImg(String urlpath, String hpath, String extension, boolean toPdf) {
        // 1. 路径处理（完全对齐 C# Split/Replace 逻辑）
        String[] hpathParts = hpath.split("/");
        String htmlName = hpathParts[hpathParts.length - 1].replace(".html", "");
        String imgFloder = hpath.replace(".html", "_imgs/");

        // 2. 创建图片文件夹（对齐 C# Directory.CreateDirectory）
        File imgDir = new File(imgFloder);
        if (!imgDir.exists()) {
            imgDir.mkdirs();
        }

        String style;

        // 3. 按文件类型处理（对齐 C# switch 逻辑）
        switch (extension.toLowerCase()) {
            case "doc":
            case "docx":
                com.spire.doc.Document document = new com.spire.doc.Document();
                PdfWriter pdfWriter = null;
                PdfDocument pdfDocument = null;
                com.itextpdf.layout.Document layoutDoc = null;

                try {
                    document.loadFromFile(urlpath);
                    style = String.format("width: %s;margin: auto;border: 1px solid #ececec;box-sizing: border-box;box-shadow: 0 0 15px 2px rgba(0,0,0,0.1);background-color:#dddddd;",
                            "280mm");

                    BufferedImage[] imgs = document.saveToImages(ImageType.Bitmap);
                    StringBuilder imgHtml = new StringBuilder();

                    // 初始化 PDF（仅当 toPdf 为 true 时）
                    if (toPdf) {
                        String pdfPath = urlpath.replace(".docx", ".pdf").replace(".doc", ".pdf");
                        pdfWriter = new PdfWriter(pdfPath);
                        pdfDocument = new PdfDocument(pdfWriter);
                        layoutDoc = new com.itextpdf.layout.Document(pdfDocument, PageSize.A4);
                        layoutDoc.setMargins(25, 25, 25, 25); // 对齐 C# 页边距
                    }

                    for (int i = 0; i < imgs.length; i++) {
                        int pageNum = i + 1;
                        BufferedImage img = imgs[i];
                        String imgPath = imgFloder + pageNum + ".jpeg";

                        // 保存图片为 JPEG
                        try (FileOutputStream fos = new FileOutputStream(imgPath)) {
                            ImageIO.write(img, "jpeg", fos);
                        }

                        imgHtml.append(String.format("<img src='%s_imgs/%d.jpeg' />", htmlName, pageNum));

                        // 图片添加到 PDF（使用 AreaBreak 新建页面）
                        if (toPdf && layoutDoc != null) {
                            if (i > 0) {
                                layoutDoc.add(new AreaBreak(AreaBreakType.NEXT_PAGE)); // 替代 newPage 方法
                            }
                            ImageData imageData = ImageDataFactory.create(imgPath);
                            Image pdfImage = new Image(imageData); // 此处导入的是 com.itextpdf.layout.element.Image

                            // 缩放适配 A4 页面
                            if (pdfImage.getImageHeight() > PageSize.A4.getHeight() - 25
                                    || pdfImage.getImageWidth() > PageSize.A4.getWidth() - 25) {
                                pdfImage.scaleToFit(PageSize.A4.getWidth() - 25, PageSize.A4.getHeight() - 25);
                            }
                            layoutDoc.add(pdfImage);
                        }

                        img.flush(); // 释放图片资源
                    }

                    // 生成 HTML
//                    String htmlContent = String.format(ImgHtml, htmlName, imgHtml, style,
//                            " <style type='text/css'>img{margin:10px auto;}</style>");
                    String htmlContent = ImgHtml.replace("${0}", htmlName) // 替换标题
                            .replace("${1}", imgHtml.toString()) // 替换 imgHtml.ToString()
                            .replace("${2}", style)
                            .replace("${3}", " <style type='text/css'>img{margin:10px auto;}</style>");
                    Files.write(Paths.get(hpath), htmlContent.getBytes(StandardCharsets.UTF_8));

                } catch (IOException e) {
                    throw new RuntimeException("Word 转图片+HTML 失败：" + e.getMessage(), e);
                } finally {
                    // 释放 Spire Word 资源（无受检异常，直接调用）
                    if (document != null) {
                        document.dispose();
                    }

                    // 处理 iText7 资源关闭的受检异常
                    try {
                        if (layoutDoc != null) {
                            layoutDoc.close();
                        }
                        if (pdfWriter != null) {
                            pdfWriter.close();
                        }
                    } catch (IOException e) {
                        // 转换为运行时异常抛出，或记录日志
                        throw new RuntimeException("关闭 PDF 资源时发生 IO 异常", e);
                    }
                }
                break;

            case "xlsx":
            case "xls":
                com.spire.xls.Workbook book = new com.spire.xls.Workbook();

                try {
                    // 加载 Excel 文件（Spire.XLS 14.8.2 兼容 API）
                    book.loadFromFile(urlpath);
                    String saveHtmlPath = hpath;
                    String pageHtml = "";

                    // 遍历工作表（对齐 C# Worksheets.Count 逻辑）
                    for (int i = 0; i < book.getWorksheets().getCount(); i++) {
                        com.spire.xls.Worksheet sheet = book.getWorksheets().get(i);
                        String imgPath = imgFloder + i + ".jpeg";

                        // 工作表转图片（Spire.XLS 14.8.2 原生方法）
                        sheet.saveToImage(imgPath);

                        // 多工作表 HTML 路径处理（对齐 C# Replace 逻辑）
                        if (i > 0) {
                            saveHtmlPath = hpath.replace(".html", "_" + i + ".html");
                        }

                        // 构建样式（对齐 C# 页边距和宽度设置）
                        double width = book.getWorksheets().get(0).getPageSetup().getPageWidth();
                        com.spire.xls.PageSetup pageSetup = sheet.getPageSetup();
                        style = String.format("width: %fpt;margin: auto;border: 1px solid #ececec;padding:%fpt %fpt %fpt %fpt ;box-sizing: border-box;box-shadow: 0 0 15px 2px rgba(0,0,0,0.1); ",
                                width, pageSetup.getTopMargin(), pageSetup.getRightMargin(), pageSetup.getBottomMargin(), pageSetup.getLeftMargin());

                        // 上下页链接（完全对齐 C# 链接拼接逻辑）
                        pageHtml = "";
                        if (i <= book.getWorksheets().getCount() - 2) {
                            pageHtml = "<a href='" + String.format("%s_%d.html", htmlName, i + 1) + "'>下一页</a>";
                        }
                        if (i > 0) {
                            pageHtml += (i == 1)
                                    ? "<a href='" + String.format("%s.html", htmlName) + "'>上一页</a>"
                                    : "<a href='" + String.format("%s_%d.html", htmlName, i - 1) + "'>上一页</a>";
                        }

                        // 生成 HTML 文件（对齐 C# File.WriteAllText 逻辑）
//                        String htmlContent = String.format(ImgHtml, htmlName,
//                                String.format("<img src='%s_imgs/%d.jpeg' />", htmlName, i),
//                                style, pageHtml);
                        String htmlContent = ImgHtml.replace("${0}", htmlName) // 替换标题
                                .replace("${1}", String.format("<img src='%s_imgs/%d.jpeg' />", htmlName, i))
                                .replace("${2}", style)
                                .replace("${3}", pageHtml);
                        Files.write(Paths.get(saveHtmlPath), htmlContent.getBytes(StandardCharsets.UTF_8));
                    }
                } catch (IOException e) {
                    throw new RuntimeException("Excel 转图片+HTML 失败：" + e.getMessage(), e);
                } finally {
                    // 释放 Excel 资源（Spire.XLS 14.8.2  dispose 方法）
                    if (book != null) {
                        book.dispose();
                    }
                }
                break;

            default:
                throw new IllegalArgumentException("不支持的文件格式：" + extension);
        }
    }

    /**
     * 检查办公文档密码是否有效（严格还原 C# 逻辑）
     *
     * @param urlpath   文档文件路径（本地路径或网络路径，需确保可访问）
     * @param extension 文档扩展名（原 C# 未统一大小写，此处保留原逻辑不转换）
     * @param password  待验证的密码（可为 null 或空字符串）
     * @return 密码正确返回 true；密码错误/文档加密但未传密码返回 false；其他异常抛出
     * @throws Exception 非密码相关的异常（如文件不存在、格式错误等）
     */
    public static boolean CheckOfficPwd(String urlpath, String extension, String password) throws Exception {
        try {
            switch (extension) {
                case "doc":
                    // 检测 DOC 文件格式及加密状态（完整包名）
                    com.aspose.words.FileFormatInfo info = com.aspose.words.FileFormatUtil.detectFileFormat(urlpath);
                    // 文档加密且未提供密码 → 返回 false（还原 C# 短路逻辑）
                    if (info.isEncrypted() && (password == null || password.isEmpty())) {
                        return false;
                    }
                    // 尝试用密码打开文档（完整包名，还原 C# 匿名 LoadOptions 配置）
                    com.aspose.words.LoadOptions docLoadOpts = new com.aspose.words.LoadOptions();
                    docLoadOpts.setPassword(password);
                    com.aspose.words.Document doc = new com.aspose.words.Document(urlpath, docLoadOpts);
                    break;

                case "xls":
                    // 检测 XLS 文件格式及加密状态（完整包名）
                    com.aspose.cells.FileFormatInfo xinfo = com.aspose.cells.FileFormatUtil.detectFileFormat(urlpath);
                    // 文档加密且未提供密码 → 返回 false（还原 C# 短路逻辑）
                    if (xinfo.isEncrypted() && (password == null || password.isEmpty())) {
                        return false;
                    }
                    // 还原 C# using 逻辑（Aspose.Cells.Workbook 按旧版本适配手动关闭）
                    com.aspose.cells.LoadOptions xlsLoadOpts = new com.aspose.cells.LoadOptions();
                    xlsLoadOpts.setPassword(password);
                    com.aspose.cells.Workbook workbook = null;
                    try {
                        workbook = new com.aspose.cells.Workbook(urlpath, xlsLoadOpts);
                    } finally {
                        // 还原 C# using 的自动释放逻辑（手动关闭资源）
                        if (workbook != null) {
                            try {
                                workbook.dispose(); // 旧版本用 close()，新版本可替换为 dispose()
                            } catch (Exception e) {
                                // 忽略关闭异常，不影响原业务逻辑（还原 C# using 特性）
                            }
                        }
                    }
                    break;

                case "pptx":
                    // 还原 C# using 逻辑（Aspose.Slides.Presentation 适配手动关闭）
                    com.aspose.slides.LoadOptions pptLoadOpts = new com.aspose.slides.LoadOptions();
                    pptLoadOpts.setPassword(password);
                    // 明确指定文件格式为 PPT（还原原 C# 注释逻辑）
                    pptLoadOpts.setLoadFormat(com.aspose.slides.LoadFormat.Ppt);

                    com.aspose.slides.Presentation presentation = null;
                    try {
                        presentation = new com.aspose.slides.Presentation(urlpath, pptLoadOpts);
                        return true; // 成功打开即返回 true（还原原 C# 分支内返回逻辑）
                    } finally {
                        // 还原 C# using 的自动释放逻辑（手动关闭资源）
                        if (presentation != null) {
                            try {
                                presentation.dispose(); // Slides 通常用 dispose() 释放
                            } catch (Exception e) {
                                // 忽略关闭异常，不影响原业务逻辑
                            }
                        }
                    }

                default:
                    // 无默认处理（还原 C# 未处理默认分支的逻辑，不抛异常）
                    break;
            }
            // 所有支持的格式成功打开后返回 true（还原原 C# 最终返回逻辑）
            return true;
        } catch (com.aspose.words.IncorrectPasswordException e) {
            // 捕获 Aspose.Words 密码错误异常 → 返回 false（还原原 C# 捕获逻辑）
            return false;
        } catch (Exception e) {
            // 捕获所有其他异常，判断消息是否包含 "password" → 返回 false（严格还原 C# IndexOf 逻辑）
            if (e.getMessage() != null && e.getMessage().contains("password")) {
                return false;
            }
            // 非密码相关异常，向上抛出（还原原 C# throw 逻辑）
            throw e;
        }
    }

    /**
     * 使用 Aspose 将 Word/Excel/PPT 转 PDF（显式声明类路径，避免包冲突）
     *
     * @param urlpath   源文件路径
     * @param hpath     生成的PDF路径
     * @param extension 文件扩展名（doc/docx/xls/xlsx/ppt/pptx）
     * @param toPdf     是否额外生成加密PDF（仅Word生效）
     */
    public static boolean saveToPdfByAspose(String urlpath, String hpath, String extension, boolean toPdf, String password) throws Exception {
        boolean IsEncrypted = false;
        String generatedPdfPath = null;
        switch (extension.toLowerCase()) {
            case "doc":
            case "docx":
                com.aspose.words.FileFormatInfo info = com.aspose.words.FileFormatUtil.detectFileFormat(urlpath);
                if (info.isEncrypted() && (password == null || password.isEmpty())) {
                    IsEncrypted = true;
                }
                com.aspose.words.Document wordDoc = null;
                try {
                    // 处理 Document 构造可能抛出的异常
//                    wordDoc = new com.aspose.words.Document(urlpath);
                    com.aspose.words.LoadOptions options = new com.aspose.words.LoadOptions();
                    options.setPassword(password);
                    wordDoc = new com.aspose.words.Document(urlpath, options);
                    // 配置 PDF 保存选项
                    com.aspose.words.PdfSaveOptions wdOptions = new com.aspose.words.PdfSaveOptions();
                    // 直接配置加密详情（仅需用户密码、所有者密码、权限）
                    com.aspose.words.PdfEncryptionDetails encryptionDetails = new com.aspose.words.PdfEncryptionDetails(
                            "",  // 用户密码（空表示无需密码打开文档）
                            "password"  // 所有者密码（用于设置权限）
                    );
                    encryptionDetails.setPermissions(
                            com.aspose.words.PdfPermissions.MODIFY_ANNOTATIONS | com.aspose.words.PdfPermissions.DOCUMENT_ASSEMBLY
                    );
                    wdOptions.setEncryptionDetails(encryptionDetails);
                    wdOptions.setCompliance(com.aspose.words.PdfCompliance.PDF_A_1_B);

                    // 处理 save 方法可能抛出的异常
                    wordDoc.save(hpath);
                    setFileFullPermission(hpath); // 设置hpath文件权限
                    generatedPdfPath = hpath;
                    if (toPdf) {
                        String pdfPath = urlpath.replace(".docx", ".pdf").replace(".doc", ".pdf");
                        wordDoc.save(pdfPath, wdOptions);
                        setFileFullPermission(hpath); // 设置hpath文件权限
                        generatedPdfPath = hpath;
                    }
                } catch (Exception e) {
                    // 将受检异常转换为运行时异常，避免方法声明 throws
                    log.debug(String.format("Word 转 PDF 失败： %s urlpath: %s", e.getMessage(), urlpath));
                    throw new RuntimeException("Word 转 PDF 失败：" + e.getMessage(), e);
                } finally {
                    // Aspose.Words 无 dispose 方法，手动置空帮助GC
                    wordDoc = null;
                }
                break;

            case "xls":
            case "xlsx":
                com.aspose.cells.Workbook excelWorkbook = null;
                try {
                    com.aspose.cells.LoadOptions options = new com.aspose.cells.LoadOptions();
                    options.setPassword(password);
                    excelWorkbook = new com.aspose.cells.Workbook(urlpath, options);
                    com.aspose.cells.PdfSaveOptions exOptions = new com.aspose.cells.PdfSaveOptions();
                    com.aspose.cells.PdfSecurityOptions securityOptions = new com.aspose.cells.PdfSecurityOptions();
                    securityOptions.setExtractContentPermission(false);
                    securityOptions.setPrintPermission(false);
                    exOptions.setSecurityOptions(securityOptions);
                    exOptions.setAllColumnsInOnePagePerSheet(true);
                    exOptions.setOnePagePerSheet(true);
                    // 修正：Aspose.Cells 中 PdfCompliance 直接位于 com.aspose.cells 包下（无 Rendering 子包）
                    exOptions.setCompliance(com.aspose.cells.PdfCompliance.PDF_A_1_B);  // 修正此处类路径
                    excelWorkbook.save(hpath, exOptions);
                    setFileFullPermission(hpath); // 设置hpath文件权限
                    generatedPdfPath = hpath;
                    IsEncrypted = excelWorkbook.getSettings().getPassword() != null;
                } catch (Exception e) {
                    throw new RuntimeException("Excel 转 PDF 失败：" + e.getMessage(), e);
                } finally {
                    if (excelWorkbook != null) {
                        excelWorkbook.dispose();
                    }
                }
                break;

            case "ppt":
            case "pptx":
                com.aspose.slides.Presentation ppt = null;
                try {
                    com.aspose.slides.LoadOptions slideLoadOptions = new com.aspose.slides.LoadOptions();
                    slideLoadOptions.setPassword(password);
                    slideLoadOptions.setLoadFormat(com.aspose.slides.LoadFormat.Ppt);
                    ppt = new com.aspose.slides.Presentation(urlpath, slideLoadOptions);
                    IsEncrypted = ppt.getProtectionManager().isEncrypted();
                    ppt.save(hpath, com.aspose.slides.SaveFormat.Pdf);
                    setFileFullPermission(hpath); // 设置hpath文件权限
                    generatedPdfPath = hpath;
                } catch (Exception e) {
                    throw new RuntimeException("PPT 转 PDF 失败：" + e.getMessage(), e);
                } finally {
                    if (ppt != null) {
                        try {
                            ppt.dispose();
                        } catch (Exception e) {
                            throw new RuntimeException("释放 PPT 资源失败：" + e.getMessage(), e);
                        }
                    }
                }
                break;
            default:
                throw new IllegalArgumentException("不支持的格式：" + extension);
        }
        if (IsEncrypted && generatedPdfPath != null) {
            String finalHpath = generatedPdfPath; // 内部类需final变量
            new Timer(true).schedule(new TimerTask() {
                @Override
                public void run() {
                    // 对应 C# 的 File.Exists(hpath) 判断文件是否存在
                    File previewFile = new File(finalHpath);
                    if (previewFile.exists()) {
                        // 对应 C# 的 File.Delete(hpath) 删除文件
                        boolean deleteSuccess = previewFile.delete();
                        // 可选：添加删除日志（如需要）
                        log.debug(String.valueOf("加密文件预览已删除：" + finalHpath + "，删除结果：" + deleteSuccess));
                    }
                }
            }, 30000); // 延迟 30000 毫秒（30秒）执行，与 C# 一致
        }
        return true;
    }

    /**
     * 核心方法：设置文件为所有用户可读可写权限（兼容Linux/Windows）
     *
     * @param filePath 文件路径
     */
    private static void setFileFullPermission(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            log.debug(String.valueOf("文件不存在，无法设置权限：" + filePath));
            return;
        }
        // 1. Windows 系统：设置文件为可读写（移除只读属性）
        if (System.getProperty("os.name").toLowerCase().contains("windows")) {
            if (file.setWritable(true, false)) { // false表示所有用户可写
                log.debug(String.valueOf("Windows：设置文件可写成功 - " + filePath));
            }
            if (file.setReadable(true, false)) { // false表示所有用户可读
                log.debug(String.valueOf("Windows：设置文件可读成功 - " + filePath));
            }
            if (file.setExecutable(true, false)) { // false表示所有用户可读
                log.debug(String.valueOf("Windows：设置文件可读成功 - " + filePath));
            }
        }
        // 2. Linux/Mac 系统：执行 chmod 666 设置权限（rw-rw-rw-）
        else {
            try {
                // 执行 chmod 666 命令
                Process process = Runtime.getRuntime().exec(new String[]{"chmod", "777", filePath});
                int exitCode = process.waitFor();
                if (exitCode == 0) {
                    log.debug(String.valueOf("Linux：设置文件权限 777 成功 - " + filePath));
                } else {
                    log.warn(String.valueOf("Linux：设置文件权限失败，退出码：" + exitCode + "，文件：" + filePath));
                }
            } catch (IOException | InterruptedException e) {
                log.warn(String.valueOf("设置Linux文件权限异常：" + e.getMessage()));
            }
        }
    }

    /**
     * 使用 Spire.Presentation 将PPT转PDF（显式声明类路径）
     *
     * @param urlpath 源PPT路径
     * @param hpath   生成的PDF路径
     */
    public static void savePptToPdfBySprie(String urlpath, String hpath) {
        // 显式使用 Spire 的 Presentation（完整路径，避免与Aspose冲突）
        com.spire.presentation.Presentation ppt = new com.spire.presentation.Presentation();
        try {
            ppt.loadFromFile(urlpath);
            // 保存为PDF（指定Spire的FileFormat）
            ppt.saveToFile(hpath, com.spire.presentation.FileFormat.PDF);
        } catch (Exception e) {
            throw new RuntimeException("PPT转PDF失败：" + e.getMessage(), e);
        } finally {
            ppt.dispose(); // 释放资源
        }
    }

    /**
     * 使用 Aspose.Words 将Word转图片并生成HTML（显式声明类路径）
     *
     * @param urlpath   源Word路径
     * @param hpath     生成的HTML路径
     * @param extension 文件扩展名（仅doc/docx）
     * @param toPdf     是否同时生成PDF
     */
    public static void saveToImgByAspose(String urlpath, String hpath, String extension, boolean toPdf) {
        // 路径处理（与 C# Split/Last/Replace 逻辑完全一致）
        String[] hpathParts = hpath.split("/");
        String htmlName = hpathParts[hpathParts.length - 1].replace(".html", "");
        String imgFloder = hpath.replace(".html", "_imgs/");
        java.io.File imgDir = new java.io.File(imgFloder);
        if (!imgDir.exists()) {
            imgDir.mkdirs();
        }

        com.aspose.words.Document wordDoc = null;
        try {
            // 初始化 Word 文档（与 C# 一致）
            wordDoc = new com.aspose.words.Document(urlpath);

            // 图片保存选项配置（完全对齐 C# 的 ImageSaveOptions）
            com.aspose.words.ImageSaveOptions imgOptions = new com.aspose.words.ImageSaveOptions(com.aspose.words.SaveFormat.JPEG);
            imgOptions.setPrettyFormat(true);
            imgOptions.setJpegQuality(100); // 与 C# JpegQuality = 100 一致
            imgOptions.setResolution(200);  // 与 C# Resolution = 200 一致

            // 构建 HTML 图片标签（与 C# StringBuilder 逻辑一致）
            StringBuilder imgHtml = new StringBuilder();
            String style = "margin: auto;border: 1px solid #ececec;box-sizing: border-box;box-shadow: 0 0 15px 2px rgba(0,0,0,0.1);";

            // 可选生成 PDF（与 C# toPdf 逻辑一致）
            if (toPdf) {
                String pdfPath = urlpath.replace(".docx", ".pdf").replace(".doc", ".pdf");
                wordDoc.save(pdfPath, com.aspose.words.SaveFormat.PDF);
            }

            // 逐页生成图片（核心修正：用 PageSet 构造函数指定页码）
            int pageCount = wordDoc.getPageCount();
            for (int i = 0; i < pageCount; i++) {
                // 修正：PageSet 无 singlePage 方法，直接用 new PageSet(i) 指定第 i 页
                imgOptions.setPageSet(new com.aspose.words.PageSet(i));
                String imgPath = imgFloder + i + ".jpeg";
                wordDoc.save(imgPath, imgOptions); // 逐页保存图片（与 C# 一致）
                imgHtml.append(String.format("<img src='%s_imgs/%d.jpeg' />", htmlName, i));
            }

            // 生成 HTML 文件（与 C# File.WriteAllText + string.Format 逻辑一致）
//            String htmlContent = String.format(ImgHtml, htmlName, imgHtml.toString(), style, "");
            String htmlContent = ImgHtml.replace("${0}", htmlName) // 替换标题
                    .replace("${1}", imgHtml.toString()) // 替换 imgHtml.ToString()
                    .replace("${2}", style)
                    .replace("${3}", "");
            java.nio.file.Files.write(
                    java.nio.file.Paths.get(hpath),
                    htmlContent.getBytes(java.nio.charset.StandardCharsets.UTF_8)
            );

        } catch (Exception e) {
            throw new RuntimeException("Word 转图片+HTML 失败：" + e.getMessage(), e);
        } finally {
            // 帮助 GC 回收资源（Java 无 using，等价于 C# 资源释放逻辑）
            wordDoc = null;
        }
    }

    /**
     * 对齐C#逻辑：Word转HTML（添加页面样式、统一编码），用Aspose.Words替代Microsoft.Office.Interop.Word
     *
     * @param filePath 源Word文件路径
     * @param htmlPath 生成的HTML保存路径
     */
    public static void docToHtmlByMicrosoftOffice(String filePath, String htmlPath) {
        if (filePath == null || filePath.isEmpty() || htmlPath == null || htmlPath.isEmpty()) {
            return;
        }

        com.aspose.words.Document wordDoc = null;
        try {
            // 显式使用Aspose.Words打开Word文档
            wordDoc = new com.aspose.words.Document(filePath);
            com.aspose.words.PageSetup pageSetup = wordDoc.getFirstSection().getPageSetup();

            // 计算样式（对齐C# PageWidth、边距逻辑）
            double width = pageSetup.getPageWidth();
            double topMargin = pageSetup.getTopMargin();
            double rightMargin = pageSetup.getRightMargin();
            double bottomMargin = pageSetup.getBottomMargin();
            double leftMargin = pageSetup.getLeftMargin();
            String style = String.format(
                    "body{width:%fpt;margin: auto;border: 1px solid #ececec;padding:%fpt %fpt %fpt %fpt;box-sizing: border-box;box-shadow: 0 0 15px 2px rgba(0,0,0,0.1);}",
                    width, topMargin, rightMargin, bottomMargin, leftMargin
            );

            // 保存为HTML（对应C# wdFormatFilteredHTML）
            com.aspose.words.HtmlSaveOptions saveOptions = new com.aspose.words.HtmlSaveOptions(com.aspose.words.SaveFormat.HTML);
            saveOptions.setExportImagesAsBase64(true); // 避免额外生成图片文件
            wordDoc.save(htmlPath, saveOptions);

            // 转换HTML编码和添加样式（调用下方transHTMLEncoding方法）
            transHTMLEncoding(htmlPath, style);
        } catch (Exception e) {
            throw new RuntimeException("Word转HTML失败：" + e.getMessage(), e);
        } finally {
            if (wordDoc != null) {
                wordDoc = null; // 帮助GC回收
            }
        }
    }

    /**
     * 对齐C#逻辑：修改HTML编码为UTF-8，注入自定义样式
     *
     * @param strFilePath HTML文件路径
     * @param style       要注入的CSS样式
     */
    private static void transHTMLEncoding(String strFilePath, String style) {
        try {
            // 读取HTML文件（对应C# StreamReader，编码适配系统默认）
            java.io.BufferedReader sr = new java.io.BufferedReader(
                    new java.io.InputStreamReader(new java.io.FileInputStream(strFilePath), java.nio.charset.StandardCharsets.UTF_8)
            );
            StringBuilder htmlBuilder = new StringBuilder();
            String line;
            while ((line = sr.readLine()) != null) {
                htmlBuilder.append(line).append("\n");
            }
            sr.close();
            String html = htmlBuilder.toString();

            // 注入样式（替换</head>标签）
            html = html.replace("</head>", "<style type='text/css'>" + style + "</style></head>");

            // 添加UTF-8编码meta标签（对应C# Regex判断）
            java.util.regex.Pattern charsetPattern = java.util.regex.Pattern.compile("<meta[^<|>]+charset=", java.util.regex.Pattern.CASE_INSENSITIVE);
            java.util.regex.Matcher matcher = charsetPattern.matcher(html);
            if (!matcher.find()) {
                html = java.util.regex.Pattern.compile("<head>", java.util.regex.Pattern.CASE_INSENSITIVE)
                        .matcher(html)
                        .replaceFirst("<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");
            }

            // 写入HTML文件（对应C# StreamWriter）
            java.io.BufferedWriter sw = new java.io.BufferedWriter(
                    new java.io.OutputStreamWriter(new java.io.FileOutputStream(strFilePath), java.nio.charset.StandardCharsets.UTF_8)
            );
            sw.write(html);
            sw.close();
        } catch (Exception ex) {
            throw new RuntimeException("HTML编码转换失败：" + ex.getMessage(), ex);
        }
    }

    /**
     * 对齐C#逻辑：调用mxcadassembly.exe转换DWG文件为MxWeb格式
     *
     * @param fileUrl    源DWG文件路径
     * @param fileName   输出文件名
     * @param dwgDocPath 输出目录路径
     * @return 转换是否成功
     */
    public static boolean convertDwgToMxWeb(String fileUrl, String fileName, String dwgDocPath) {
        // 对应C# WebConfigUtil.ServerPath（需用户替换为Java项目的根路径）
        String serverPath = System.getProperty("user.dir"); // 临时用项目根路径，需根据实际调整
        String exePath = new java.io.File(serverPath + "/bin/MxCad/mxcadassembly.exe").getAbsolutePath();

        // 检查exe文件是否存在
        java.io.File exeFile = new java.io.File(exePath);
        if (!exeFile.exists()) {
            return false;
        }

        // 构造JSON参数（对齐C# arguments格式）
        String arguments = String.format(
                "{\"srcpath\":\"%s\",\"outpath\":\"%s\",\"outname\":\"%s\",\"compression\":0}",
                fileUrl.replace("\\", "/"), // Java路径统一用/
                dwgDocPath.replace("\\", "/"),
                fileName
        );

        // 启动exe进程（对应C# Process）
        java.lang.ProcessBuilder processBuilder = new java.lang.ProcessBuilder(exePath, arguments);
        processBuilder.redirectErrorStream(true); // 合并错误流
        java.lang.Process process = null;
        try {
            process = processBuilder.start();
            process.waitFor(); // 等待进程执行完成
            return process.exitValue() == 0; // 退出码0表示成功
        } catch (Exception ex) {
            throw new RuntimeException("DWG转MxWeb失败：" + ex.getMessage(), ex);
        } finally {
            if (process != null) {
                process.destroy(); // 销毁进程
            }
        }
    }

    /**
     * 对齐C#逻辑：用Aspose.Cells/Aspose.Words将Excel/Word转HTML
     *
     * @param filePath 源文件路径
     * @param savePath 生成的HTML保存路径
     * @param fType    文件类型（xlsx/xls/doc/docx）
     */
    public static void saveToHtmlByAspose(String filePath, String savePath, String fType) {
        switch (fType.toLowerCase()) {
            case "xlsx":
            case "xls":
                // 显式使用Aspose.Cells处理Excel转HTML
                com.aspose.cells.Workbook excelWorkbook = null;
                try {
                    excelWorkbook = new com.aspose.cells.Workbook(filePath);
                    com.aspose.cells.HtmlSaveOptions xOptions = new com.aspose.cells.HtmlSaveOptions();
                    xOptions.setExportGridLines(true);
                    xOptions.setPresentationPreference(true);
                    xOptions.setExportWorksheetCSSSeparately(false);
                    xOptions.setExportImagesAsBase64(true);

                    excelWorkbook.save(savePath, xOptions);
                } catch (Exception e) {
                    throw new RuntimeException("Excel转HTML失败：" + e.getMessage(), e);
                } finally {
                    if (excelWorkbook != null) {
                        excelWorkbook.dispose();
                    }
                }
                break;

            case "doc":
            case "docx":
                // 显式使用Aspose.Words处理Word转HTML
                com.aspose.words.Document wordDoc = null;
                try {
                    wordDoc = new com.aspose.words.Document(filePath);
                    com.aspose.words.HtmlSaveOptions options = new com.aspose.words.HtmlSaveOptions();
                    options.setExportImagesAsBase64(true);
                    options.setCssStyleSheetType(com.aspose.words.CssStyleSheetType.INLINE);
                    options.setPrettyFormat(true);

                    wordDoc.save(savePath, options);
                } catch (Exception e) {
                    throw new RuntimeException("Word转HTML失败：" + e.getMessage(), e);
                } finally {
                    wordDoc = null;
                }
                break;

            default:
                throw new IllegalArgumentException("不支持的文件类型：" + fType);
        }
    }

    /**
     * 对齐C#逻辑：获取视频时长（秒），用javacv-ffmpeg替代MediaToolkit
     *
     * @param filePath 视频文件路径
     * @return 视频时长（秒），不支持格式返回0
     */
    public static long getVideoInfo(String filePath) {
        // 1. 初始化文件对象，定义支持的视频格式（完全对齐C#的types字符串）
        File file = new File(filePath);
        String supportTypes = ".avi,.wmv,.mpeg,.mp4,.m4v,.mov,.asf,.flv,.f4v,.rmvb,.rm,.3gp,.vob";

        // 2. 判断文件扩展名是否在支持列表中（对齐C#的IndexOf逻辑）
        String fileExtension = file.exists() ? file.getName().substring(file.getName().lastIndexOf(".")) : "";
        if (fileExtension != null && !fileExtension.isEmpty() && supportTypes.contains(fileExtension)) {
            try {
                // 3. 第一步：调用自定义工具类获取时长（对齐C#的FormatFactoryUtil.GetVideoTime）
                long totalTime = ToInt64(FormatFactoryUtil.getVideoTime(filePath));
                if (totalTime > 0) {
                    return totalTime;
                }

                // 4. 第二步：JavaCV兜底（对齐C#的MediaToolkit逻辑）
                FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filePath);
                try {
                    grabber.start();
                    // 转换时长（微秒转秒，对齐C#的Ticks/10000000的单位转换逻辑）
                    long duration = grabber.getLengthInTime() / 1000000;
                    return duration > 0 ? duration : 0;
                } finally {
                    if (grabber != null) {
                        try {
                            grabber.stop();
                            grabber.release();
                        } catch (Exception e) {
                            // 忽略释放异常，对齐C#的using自动释放
                        }
                    }
                }
            } catch (Exception e) {
                // 捕获所有异常返回0（对齐C#的catch逻辑）
                return 0;
            }
        } else {
            // 扩展名不支持返回0
            return 0;
        }
    }

    /**
     * 对齐C#逻辑：生成二维码，支持叠加Logo，用ZXing替代QRCodeEncoder
     *
     * @param str        二维码内容
     * @param scale      缩放比例（<=0时默认4）
     * @param logoimgurl Logo图片路径（为空则不叠加）
     * @return 生成的二维码Bitmap
     */
    public static java.awt.image.BufferedImage makeQRCode(String str, int scale, String logoimgurl) {
        if (str == null || str.isEmpty()) {
            return null;
        }

        // 处理缩放比例（对齐C#逻辑）
        int qrScale = scale <= 0 ? 4 : scale;

        // 处理URL编码（对齐C#逻辑）
        if (str.toLowerCase().startsWith("http")) {
            try {
                java.net.URI uri = new java.net.URI(str);
                StringBuilder newStr = new StringBuilder();
                newStr.append(uri.getScheme()).append("://").append(uri.getAuthority());
                String[] pathParts = uri.getPath().split("/");
                for (String part : pathParts) {
                    if (!part.isEmpty()) {
                        newStr.append("/").append(java.net.URLEncoder.encode(part, "UTF-8").replace("+", "%20"));
                    }
                }
                newStr.append(uri.getQuery() != null ? "?" + uri.getQuery() : "");
                str = newStr.toString();
            } catch (Exception e) {
                throw new RuntimeException("URL编码失败：" + e.getMessage(), e);
            }
        }

        // 生成二维码（ZXing配置，对齐C# QRCodeEncoder参数）
        com.google.zxing.Writer writer = new com.google.zxing.qrcode.QRCodeWriter();
        com.google.zxing.BarcodeFormat format = com.google.zxing.BarcodeFormat.QR_CODE;
        com.google.zxing.EncodeHintType errorCorrection = com.google.zxing.EncodeHintType.ERROR_CORRECTION;
        com.google.zxing.qrcode.decoder.ErrorCorrectionLevel errorLevel = com.google.zxing.qrcode.decoder.ErrorCorrectionLevel.M; // 对应C# ERROR_CORRECTION.M

        java.util.Map<com.google.zxing.EncodeHintType, Object> hints = new java.util.HashMap<>();
        hints.put(errorCorrection, errorLevel);
        hints.put(com.google.zxing.EncodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(com.google.zxing.EncodeHintType.MARGIN, 1);

        com.google.zxing.common.BitMatrix bitMatrix;
        try {
            bitMatrix = writer.encode(str, format, 300, 300, hints); // 300x300基础尺寸
        } catch (com.google.zxing.WriterException e) {
            throw new RuntimeException("二维码生成失败：" + e.getMessage(), e);
        }

        // 转换为BufferedImage（对应C# Bitmap）
        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        java.awt.image.BufferedImage qrImage = new java.awt.image.BufferedImage(width, height, java.awt.image.BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                qrImage.setRGB(x, y, bitMatrix.get(x, y) ? java.awt.Color.BLACK.getRGB() : java.awt.Color.WHITE.getRGB());
            }
        }

        // 叠加Logo（对齐C#逻辑）
        if (logoimgurl != null && !logoimgurl.isEmpty()) {
            java.io.File logoFile = new java.io.File(logoimgurl);
            if (logoFile.exists()) {
                int logoWh = 7 * qrScale;
                java.awt.image.BufferedImage logoImg;
                try {
                    logoImg = javax.imageio.ImageIO.read(logoFile); // 捕获IO异常
                } catch (java.io.IOException e) {
                    throw new RuntimeException("Logo图片读取失败：" + e.getMessage(), e);
                }

                // 缩放Logo
                java.awt.Image scaledLogo = logoImg.getScaledInstance(logoWh, logoWh, java.awt.Image.SCALE_SMOOTH);
                java.awt.image.BufferedImage bLogo = new java.awt.image.BufferedImage(logoWh, logoWh, java.awt.image.BufferedImage.TYPE_INT_ARGB);
                java.awt.Graphics2D g2d = bLogo.createGraphics();
                g2d.drawImage(scaledLogo, 0, 0, null);
                g2d.dispose();

                // 绘制Logo到二维码中心
                java.awt.Graphics g = qrImage.getGraphics();
                int x = (width - logoWh) / 2;
                int y = (height - logoWh) / 2;
                g.drawImage(bLogo, x, y, null);
                g.dispose();

                logoImg.flush();
                bLogo.flush();
            }
        }

        return qrImage;
    }


}
