|  | @@ -61,131 +61,133 @@ public class HtmlTableToExcelConverter {
 | 
	
		
			
				|  |  |          Map<Integer, Set<Integer>> occupiedCells = new HashMap<>();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          int excelRowNum = 0;
 | 
	
		
			
				|  |  | -        for (Element tr : table.select("tr")) {
 | 
	
		
			
				|  |  | -            // 跳过空行
 | 
	
		
			
				|  |  | -            if (tr.children().isEmpty()) {
 | 
	
		
			
				|  |  | -                excelRowNum++;
 | 
	
		
			
				|  |  | -                continue;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +        if (table != null) {
 | 
	
		
			
				|  |  | +            for (Element tr : table.select("tr")) {
 | 
	
		
			
				|  |  | +                // 跳过空行
 | 
	
		
			
				|  |  | +                if (tr.children().isEmpty()) {
 | 
	
		
			
				|  |  | +                    excelRowNum++;
 | 
	
		
			
				|  |  | +                    continue;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            // 创建行并设置默认行高(比默认值小)
 | 
	
		
			
				|  |  | -            Row excelRow = sheet.createRow(excelRowNum);
 | 
	
		
			
				|  |  | +                // 创建行并设置默认行高(比默认值小)
 | 
	
		
			
				|  |  | +                Row excelRow = sheet.createRow(excelRowNum);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            // 初始化当前行占用集合
 | 
	
		
			
				|  |  | -            occupiedCells.putIfAbsent(excelRowNum, new HashSet<>());
 | 
	
		
			
				|  |  | +                // 初始化当前行占用集合
 | 
	
		
			
				|  |  | +                occupiedCells.putIfAbsent(excelRowNum, new HashSet<>());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            int excelColNum = 0;
 | 
	
		
			
				|  |  | +                int excelColNum = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            //是否设置行高
 | 
	
		
			
				|  |  | -            Boolean isHeight = false;
 | 
	
		
			
				|  |  | +                //是否设置行高
 | 
	
		
			
				|  |  | +                Boolean isHeight = false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            for (Element td : tr.select("td")) {
 | 
	
		
			
				|  |  | +                for (Element td : tr.select("td")) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                //设置行高
 | 
	
		
			
				|  |  | -                String style1 = td.attr("style");
 | 
	
		
			
				|  |  | -                if (!isHeight && StringUtil.isNotBlank(style1)) {
 | 
	
		
			
				|  |  | -                    List<String> collect = Arrays.stream(style1.split(";")).collect(Collectors.toList());
 | 
	
		
			
				|  |  | -                    HashMap<String, String> map = new HashMap<>();
 | 
	
		
			
				|  |  | -                    collect.forEach(s -> {
 | 
	
		
			
				|  |  | -                        map.put(s.split(":")[0], s.split(":")[1]);
 | 
	
		
			
				|  |  | -                    });
 | 
	
		
			
				|  |  | +                    //设置行高
 | 
	
		
			
				|  |  | +                    String style1 = td.attr("style");
 | 
	
		
			
				|  |  | +                    if (!isHeight && StringUtil.isNotBlank(style1)) {
 | 
	
		
			
				|  |  | +                        List<String> collect = Arrays.stream(style1.split(";")).collect(Collectors.toList());
 | 
	
		
			
				|  |  | +                        HashMap<String, String> map = new HashMap<>();
 | 
	
		
			
				|  |  | +                        collect.forEach(s -> {
 | 
	
		
			
				|  |  | +                            map.put(s.split(":")[0], s.split(":")[1]);
 | 
	
		
			
				|  |  | +                        });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    String height = map.get("height");
 | 
	
		
			
				|  |  | -                    Float rowHeight = 16f; // 默认16点
 | 
	
		
			
				|  |  | -                    if (StringUtil.isNotBlank(height)) {
 | 
	
		
			
				|  |  | -                        isHeight = true;
 | 
	
		
			
				|  |  | -                        height = height.replace("px", "");
 | 
	
		
			
				|  |  | -                        try {
 | 
	
		
			
				|  |  | -                            Float px = Float.valueOf(height);
 | 
	
		
			
				|  |  | -                            rowHeight = px * 0.75f; // 像素转点 (1px ≈ 0.75pt)
 | 
	
		
			
				|  |  | -                        } catch (Exception ignored) {
 | 
	
		
			
				|  |  | -                            ignored.printStackTrace();
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | +                        String height = map.get("height");
 | 
	
		
			
				|  |  | +                        Float rowHeight = 16f; // 默认16点
 | 
	
		
			
				|  |  | +                        if (StringUtil.isNotBlank(height)) {
 | 
	
		
			
				|  |  | +                            isHeight = true;
 | 
	
		
			
				|  |  | +                            height = height.replace("px", "");
 | 
	
		
			
				|  |  | +                            try {
 | 
	
		
			
				|  |  | +                                Float px = Float.valueOf(height);
 | 
	
		
			
				|  |  | +                                rowHeight = px * 0.75f; // 像素转点 (1px ≈ 0.75pt)
 | 
	
		
			
				|  |  | +                            } catch (Exception ignored) {
 | 
	
		
			
				|  |  | +                                ignored.printStackTrace();
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                        excelRow.setHeightInPoints(rowHeight);
 | 
	
		
			
				|  |  | +                            excelRow.setHeightInPoints(rowHeight);
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // 跳过已被占用的单元格
 | 
	
		
			
				|  |  | -                while (isCellOccupied(occupiedCells, excelRowNum, excelColNum)) {
 | 
	
		
			
				|  |  | -                    excelColNum++;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +                    // 跳过已被占用的单元格
 | 
	
		
			
				|  |  | +                    while (isCellOccupied(occupiedCells, excelRowNum, excelColNum)) {
 | 
	
		
			
				|  |  | +                        excelColNum++;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // 处理列跨度和行跨度
 | 
	
		
			
				|  |  | -                int colspan = getSpan(td, "colspan");
 | 
	
		
			
				|  |  | -                int rowspan = getSpan(td, "rowspan");
 | 
	
		
			
				|  |  | +                    // 处理列跨度和行跨度
 | 
	
		
			
				|  |  | +                    int colspan = getSpan(td, "colspan");
 | 
	
		
			
				|  |  | +                    int rowspan = getSpan(td, "rowspan");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // 获取单元格内容
 | 
	
		
			
				|  |  | -                String cellText = extractCellText(td);
 | 
	
		
			
				|  |  | +                    // 获取单元格内容
 | 
	
		
			
				|  |  | +                    String cellText = extractCellText(td);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // 创建单元格
 | 
	
		
			
				|  |  | -                Cell cell = excelRow.createCell(excelColNum);
 | 
	
		
			
				|  |  | -                cell.setCellValue(cellText);
 | 
	
		
			
				|  |  | +                    // 创建单元格
 | 
	
		
			
				|  |  | +                    Cell cell = excelRow.createCell(excelColNum);
 | 
	
		
			
				|  |  | +                    cell.setCellValue(cellText);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // 应用样式
 | 
	
		
			
				|  |  | -                String styleKey = getCellStyleKey(td, cssRules);
 | 
	
		
			
				|  |  | -                if (!styleCache.containsKey(styleKey)) {
 | 
	
		
			
				|  |  | -                    CellStyle style = createCellStyle(workbook, td, cssRules, fontCache);
 | 
	
		
			
				|  |  | -                    styleCache.put(styleKey, style);
 | 
	
		
			
				|  |  | +                    // 应用样式
 | 
	
		
			
				|  |  | +                    String styleKey = getCellStyleKey(td, cssRules);
 | 
	
		
			
				|  |  | +                    if (!styleCache.containsKey(styleKey)) {
 | 
	
		
			
				|  |  | +                        CellStyle style = createCellStyle(workbook, td, cssRules, fontCache);
 | 
	
		
			
				|  |  | +                        styleCache.put(styleKey, style);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    // 检查是否需要自动换行
 | 
	
		
			
				|  |  | -                    if (shouldWrapText(td, cssRules, cellText)) {
 | 
	
		
			
				|  |  | -                        style.setWrapText(true);
 | 
	
		
			
				|  |  | +                        // 检查是否需要自动换行
 | 
	
		
			
				|  |  | +                        if (shouldWrapText(td, cssRules, cellText)) {
 | 
	
		
			
				|  |  | +                            style.setWrapText(true);
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                cell.setCellStyle(styleCache.get(styleKey));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                // 记录列宽(优先使用HTML中的宽度)
 | 
	
		
			
				|  |  | -                if (td.hasAttr("width")) {
 | 
	
		
			
				|  |  | -                    String widthStr = td.attr("width").replace("px", "");
 | 
	
		
			
				|  |  | -                    try {
 | 
	
		
			
				|  |  | -                        float px = Float.parseFloat(widthStr);
 | 
	
		
			
				|  |  | -                        float charWidth = px / 7f; // 像素转字符宽度 (1字符≈7px)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                        // 考虑跨列情况:总宽度分配到各列
 | 
	
		
			
				|  |  | -                        for (int i = 0; i < colspan; i++) {
 | 
	
		
			
				|  |  | -                            int colIdx = excelColNum + i;
 | 
	
		
			
				|  |  | -                            float perColWidth = charWidth / colspan;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                            // 取最大宽度作为列宽
 | 
	
		
			
				|  |  | -                            columnWidths.putIfAbsent(colIdx, 0f);
 | 
	
		
			
				|  |  | -                            if (perColWidth > columnWidths.get(colIdx)) {
 | 
	
		
			
				|  |  | -                                columnWidths.put(colIdx, perColWidth);
 | 
	
		
			
				|  |  | +                    cell.setCellStyle(styleCache.get(styleKey));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    // 记录列宽(优先使用HTML中的宽度)
 | 
	
		
			
				|  |  | +                    if (td.hasAttr("width")) {
 | 
	
		
			
				|  |  | +                        String widthStr = td.attr("width").replace("px", "");
 | 
	
		
			
				|  |  | +                        try {
 | 
	
		
			
				|  |  | +                            float px = Float.parseFloat(widthStr);
 | 
	
		
			
				|  |  | +                            float charWidth = px / 7f; // 像素转字符宽度 (1字符≈7px)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                            // 考虑跨列情况:总宽度分配到各列
 | 
	
		
			
				|  |  | +                            for (int i = 0; i < colspan; i++) {
 | 
	
		
			
				|  |  | +                                int colIdx = excelColNum + i;
 | 
	
		
			
				|  |  | +                                float perColWidth = charWidth / colspan;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                                // 取最大宽度作为列宽
 | 
	
		
			
				|  |  | +                                columnWidths.putIfAbsent(colIdx, 0f);
 | 
	
		
			
				|  |  | +                                if (perColWidth > columnWidths.get(colIdx)) {
 | 
	
		
			
				|  |  | +                                    columnWidths.put(colIdx, perColWidth);
 | 
	
		
			
				|  |  | +                                }
 | 
	
		
			
				|  |  |                              }
 | 
	
		
			
				|  |  | +                        } catch (NumberFormatException ignored) {
 | 
	
		
			
				|  |  |                          }
 | 
	
		
			
				|  |  | -                    } catch (NumberFormatException ignored) {
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // 标记当前单元格占据的所有位置
 | 
	
		
			
				|  |  | -                markCellsAsOccupied(occupiedCells, excelRowNum, excelColNum, rowspan, colspan);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                // 创建合并区域
 | 
	
		
			
				|  |  | -                if (colspan > 1 || rowspan > 1) {
 | 
	
		
			
				|  |  | -                    CellRangeAddress region = new CellRangeAddress(
 | 
	
		
			
				|  |  | -                            excelRowNum,
 | 
	
		
			
				|  |  | -                            excelRowNum + rowspan - 1,
 | 
	
		
			
				|  |  | -                            excelColNum,
 | 
	
		
			
				|  |  | -                            excelColNum + colspan - 1
 | 
	
		
			
				|  |  | -                    );
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                    // 检查是否与现有区域重叠
 | 
	
		
			
				|  |  | -                    if (!isOverlapping(mergedRegions, region)) {
 | 
	
		
			
				|  |  | -                        sheet.addMergedRegion(region);
 | 
	
		
			
				|  |  | -                        mergedRegions.add(region);
 | 
	
		
			
				|  |  | -                        CellStyle cellStyle = styleCache.get(styleKey);
 | 
	
		
			
				|  |  | -                        mergedFrame.put(region, cellStyle);
 | 
	
		
			
				|  |  | -                        // 为合并区域设置边框(使用左上角单元格的样式)
 | 
	
		
			
				|  |  | -//                        setMergedRegionBorders(workbook, sheet, region, styleCache.get(styleKey));
 | 
	
		
			
				|  |  | +                    // 标记当前单元格占据的所有位置
 | 
	
		
			
				|  |  | +                    markCellsAsOccupied(occupiedCells, excelRowNum, excelColNum, rowspan, colspan);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    // 创建合并区域
 | 
	
		
			
				|  |  | +                    if (colspan > 1 || rowspan > 1) {
 | 
	
		
			
				|  |  | +                        CellRangeAddress region = new CellRangeAddress(
 | 
	
		
			
				|  |  | +                                excelRowNum,
 | 
	
		
			
				|  |  | +                                excelRowNum + rowspan - 1,
 | 
	
		
			
				|  |  | +                                excelColNum,
 | 
	
		
			
				|  |  | +                                excelColNum + colspan - 1
 | 
	
		
			
				|  |  | +                        );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                        // 检查是否与现有区域重叠
 | 
	
		
			
				|  |  | +                        if (!isOverlapping(mergedRegions, region)) {
 | 
	
		
			
				|  |  | +                            sheet.addMergedRegion(region);
 | 
	
		
			
				|  |  | +                            mergedRegions.add(region);
 | 
	
		
			
				|  |  | +                            CellStyle cellStyle = styleCache.get(styleKey);
 | 
	
		
			
				|  |  | +                            mergedFrame.put(region, cellStyle);
 | 
	
		
			
				|  |  | +                            // 为合并区域设置边框(使用左上角单元格的样式)
 | 
	
		
			
				|  |  | +    //                        setMergedRegionBorders(workbook, sheet, region, styleCache.get(styleKey));
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                excelColNum += colspan;
 | 
	
		
			
				|  |  | +                    excelColNum += colspan;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                excelRowNum++;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -            excelRowNum++;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          // 修复合并单元格边框问题
 | 
	
		
			
				|  |  |          fixMergedRegionBorders(workbook, sheet, mergedRegions, mergedFrame);
 |