Showing
8 changed files
with
363 additions
and
60 deletions
| ... | ... | @@ -820,7 +820,7 @@ public class ContractDistributorStandardController extends DefaultBaseController |
| 820 | 820 | formulaComponentList.add(formulaComponent); |
| 821 | 821 | } |
| 822 | 822 | |
| 823 | - String latex = LatexFormulaExcelExporterUtil.convertToLatex(formulaComponentList); | |
| 823 | + String latex = LatexFormulaExcelExporterUtil.convertToContractSpecLatex(formulaComponentList); | |
| 824 | 824 | if (StringUtils.isNotBlank(latex)) { |
| 825 | 825 | LatexFormulaExcelExporterUtil.insertLatexImageToCell(workbook, sheet, latex, startRow, 5);// E列 |
| 826 | 826 | } |
| ... | ... | @@ -879,7 +879,7 @@ public class ContractDistributorStandardController extends DefaultBaseController |
| 879 | 879 | formulaComponentList.add(formulaComponent); |
| 880 | 880 | } |
| 881 | 881 | |
| 882 | - String latex = LatexFormulaExcelExporterUtil.convertToLatex(formulaComponentList); | |
| 882 | + String latex = LatexFormulaExcelExporterUtil.convertToContractSpecLatex(formulaComponentList); | |
| 883 | 883 | if (StringUtils.isNotBlank(latex)) { |
| 884 | 884 | LatexFormulaExcelExporterUtil.insertLatexImageToCell(workbook, sheet, latex, startRow, 9);// E列 |
| 885 | 885 | } |
| ... | ... | @@ -1067,6 +1067,7 @@ public class ContractDistributorStandardController extends DefaultBaseController |
| 1067 | 1067 | |
| 1068 | 1068 | try { |
| 1069 | 1069 | Sheet sheet = workbook.getSheetAt(0); |
| 1070 | + List<SpecImageCell> specImageCells = new ArrayList<>(); | |
| 1070 | 1071 | DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
| 1071 | 1072 | int startRow = 6; |
| 1072 | 1073 | String contractTitle = "销售订单(经销)"; |
| ... | ... | @@ -1099,9 +1100,9 @@ public class ContractDistributorStandardController extends DefaultBaseController |
| 1099 | 1100 | addFormulaComponent(formulaComponentList, line.getWidth(), line.getWidthTolPos(), line.getWidthTolNeg()); |
| 1100 | 1101 | addFormulaComponent(formulaComponentList, line.getLength(), line.getLengthTolPos(), line.getLengthTolNeg()); |
| 1101 | 1102 | |
| 1102 | - String latex = LatexFormulaExcelExporterUtil.convertToLatex(formulaComponentList); | |
| 1103 | + String latex = LatexFormulaExcelExporterUtil.convertToContractSpecLatexSingleLineFixed3(formulaComponentList); | |
| 1103 | 1104 | if (StringUtils.isNotBlank(latex)) { |
| 1104 | - LatexFormulaExcelExporterUtil.insertLatexImageToCell(workbook, sheet, latex, startRow, 5); | |
| 1105 | + specImageCells.add(new SpecImageCell(startRow, 5, latex)); | |
| 1105 | 1106 | } |
| 1106 | 1107 | |
| 1107 | 1108 | setCellValue(sheet, startRow, 6, line.getMaterialCode()); |
| ... | ... | @@ -1143,9 +1144,9 @@ public class ContractDistributorStandardController extends DefaultBaseController |
| 1143 | 1144 | addFormulaComponent(formulaComponentList, line.getWidth(), line.getWidthTolPos(), line.getWidthTolNeg()); |
| 1144 | 1145 | addFormulaComponent(formulaComponentList, line.getLength(), line.getLengthTolPos(), line.getLengthTolNeg()); |
| 1145 | 1146 | |
| 1146 | - String latex = LatexFormulaExcelExporterUtil.convertToLatex(formulaComponentList); | |
| 1147 | + String latex = LatexFormulaExcelExporterUtil.convertToContractSpecLatexSingleLineFixed3(formulaComponentList); | |
| 1147 | 1148 | if (StringUtils.isNotBlank(latex)) { |
| 1148 | - LatexFormulaExcelExporterUtil.insertLatexImageToCell(workbook, sheet, latex, startRow, 9); | |
| 1149 | + specImageCells.add(new SpecImageCell(startRow, 9, latex)); | |
| 1149 | 1150 | } |
| 1150 | 1151 | setCellValue(sheet, startRow, 10, line.getMaterialCode()); |
| 1151 | 1152 | setCellValue(sheet, startRow, 11, line.getProductStatus()); |
| ... | ... | @@ -1158,6 +1159,15 @@ public class ContractDistributorStandardController extends DefaultBaseController |
| 1158 | 1159 | } |
| 1159 | 1160 | } |
| 1160 | 1161 | |
| 1162 | + double specMinScale = 1.0; | |
| 1163 | + for (SpecImageCell specCell : specImageCells) { | |
| 1164 | + double scale = LatexFormulaExcelExporterUtil.calculateFitScale(workbook, sheet, specCell.latex, specCell.rowIndex, specCell.colIndex); | |
| 1165 | + specMinScale = Math.min(specMinScale, scale); | |
| 1166 | + } | |
| 1167 | + for (SpecImageCell specCell : specImageCells) { | |
| 1168 | + LatexFormulaExcelExporterUtil.insertLatexImageToCellWithMaxScale(workbook, sheet, specCell.latex, specCell.rowIndex, specCell.colIndex, specMinScale); | |
| 1169 | + } | |
| 1170 | + | |
| 1161 | 1171 | // --- 填充全局变量 --- |
| 1162 | 1172 | Map<String, Object> dataMap = buildDataMap(data, contractTitle, dateFormatter); |
| 1163 | 1173 | processTemplate(workbook, dataMap); |
| ... | ... | @@ -1210,6 +1220,18 @@ public class ContractDistributorStandardController extends DefaultBaseController |
| 1210 | 1220 | } |
| 1211 | 1221 | } |
| 1212 | 1222 | |
| 1223 | + private static class SpecImageCell { | |
| 1224 | + private final int rowIndex; | |
| 1225 | + private final int colIndex; | |
| 1226 | + private final String latex; | |
| 1227 | + | |
| 1228 | + private SpecImageCell(int rowIndex, int colIndex, String latex) { | |
| 1229 | + this.rowIndex = rowIndex; | |
| 1230 | + this.colIndex = colIndex; | |
| 1231 | + this.latex = latex; | |
| 1232 | + } | |
| 1233 | + } | |
| 1234 | + | |
| 1213 | 1235 | private void addFormulaComponent(List<LatexFormulaExcelExporterUtil.FormulaComponent> list, |
| 1214 | 1236 | BigDecimal base, BigDecimal sup, BigDecimal sub) { |
| 1215 | 1237 | if (base != null) { | ... | ... |
| ... | ... | @@ -4,6 +4,8 @@ import lombok.Data; |
| 4 | 4 | import lombok.extern.slf4j.Slf4j; |
| 5 | 5 | import org.apache.commons.collections4.CollectionUtils; |
| 6 | 6 | import org.apache.poi.ss.usermodel.*; |
| 7 | +import org.apache.poi.ss.usermodel.Font; | |
| 8 | +import org.apache.poi.ss.util.CellRangeAddress; | |
| 7 | 9 | import org.scilab.forge.jlatexmath.TeXConstants; |
| 8 | 10 | import org.scilab.forge.jlatexmath.TeXFormula; |
| 9 | 11 | import org.scilab.forge.jlatexmath.TeXIcon; |
| ... | ... | @@ -60,6 +62,85 @@ public class LatexFormulaExcelExporterUtil { |
| 60 | 62 | return latex.toString(); |
| 61 | 63 | } |
| 62 | 64 | |
| 65 | + public static String convertToContractSpecLatex(List<FormulaComponent> componentList) { | |
| 66 | + if (CollectionUtils.isEmpty(componentList)) { | |
| 67 | + return ""; | |
| 68 | + } | |
| 69 | + if (componentList.size() <= 2) { | |
| 70 | + return convertToLatex(componentList); | |
| 71 | + } | |
| 72 | + String line1 = buildLatexLine(componentList, 0, 2); | |
| 73 | + String line2 = buildLatexLine(componentList, 2, componentList.size()); | |
| 74 | + if (line2.isEmpty()) { | |
| 75 | + return "\\mathbf{" + line1 + "}"; | |
| 76 | + } | |
| 77 | + return "\\begin{array}{c}\\mathbf{" + line1 + "}\\\\\\mathbf{" + line2 + "}\\end{array}"; | |
| 78 | + } | |
| 79 | + | |
| 80 | + public static String convertToContractSpecLatexSingleLineFixed3(List<FormulaComponent> componentList) { | |
| 81 | + if (CollectionUtils.isEmpty(componentList)) { | |
| 82 | + return ""; | |
| 83 | + } | |
| 84 | + | |
| 85 | + int nonNullCount = 0; | |
| 86 | + for (FormulaComponent comp : componentList) { | |
| 87 | + if (comp != null && comp.getBase() != null) { | |
| 88 | + nonNullCount++; | |
| 89 | + } | |
| 90 | + } | |
| 91 | + if (nonNullCount <= 0) { | |
| 92 | + return ""; | |
| 93 | + } | |
| 94 | + | |
| 95 | + String actual = buildLatexLine(componentList, 0, componentList.size()); | |
| 96 | + if (actual.isEmpty()) { | |
| 97 | + return ""; | |
| 98 | + } | |
| 99 | + | |
| 100 | + int missing = Math.max(0, 3 - nonNullCount); | |
| 101 | + if (missing > 0) { | |
| 102 | + StringBuilder phantom = new StringBuilder(); | |
| 103 | + for (int i = 0; i < missing; i++) { | |
| 104 | + phantom.append(" \\times 000^{+00}_{+00}"); | |
| 105 | + } | |
| 106 | + actual = actual + "\\phantom{" + phantom + "}"; | |
| 107 | + } | |
| 108 | + | |
| 109 | + return "\\mathbf{" + actual + "}"; | |
| 110 | + } | |
| 111 | + | |
| 112 | + private static String buildLatexLine(List<FormulaComponent> componentList, int startIndex, int endIndexExclusive) { | |
| 113 | + StringBuilder latex = new StringBuilder(); | |
| 114 | + boolean first = true; | |
| 115 | + int end = Math.min(componentList.size(), endIndexExclusive); | |
| 116 | + for (int i = startIndex; i < end; i++) { | |
| 117 | + FormulaComponent comp = componentList.get(i); | |
| 118 | + if (comp == null || comp.getBase() == null) { | |
| 119 | + continue; | |
| 120 | + } | |
| 121 | + if (!first) { | |
| 122 | + latex.append(" \\times "); | |
| 123 | + } | |
| 124 | + first = false; | |
| 125 | + | |
| 126 | + latex.append(formatScientificNotation(comp.getBase())); | |
| 127 | + if (comp.getSup() != null && comp.getSup().compareTo(BigDecimal.ZERO) >= 0) { | |
| 128 | + latex.append("^{+").append(formatScientificNotation(comp.getSup())).append("}"); | |
| 129 | + } | |
| 130 | + if (comp.getSup() != null && comp.getSup().compareTo(BigDecimal.ZERO) < 0) { | |
| 131 | + latex.append("^{").append(formatScientificNotation(comp.getSup())).append("}"); | |
| 132 | + } | |
| 133 | + | |
| 134 | + if (comp.getSub() != null && comp.getSub().compareTo(BigDecimal.ZERO) >= 0) { | |
| 135 | + latex.append("_{+").append(formatScientificNotation(comp.getSub())).append("}"); | |
| 136 | + } | |
| 137 | + if (comp.getSub() != null && comp.getSub().compareTo(BigDecimal.ZERO) < 0) { | |
| 138 | + latex.append("_{").append(formatScientificNotation(comp.getSub())).append("}"); | |
| 139 | + } | |
| 140 | + } | |
| 141 | + return latex.toString(); | |
| 142 | + } | |
| 143 | + | |
| 63 | 144 | |
| 64 | 145 | /** |
| 65 | 146 | * 处理科学计数法表示的大数字 |
| ... | ... | @@ -141,25 +222,142 @@ public class LatexFormulaExcelExporterUtil { |
| 141 | 222 | * 将LaTeX公式作为图片插入到指定单元格 |
| 142 | 223 | */ |
| 143 | 224 | public static void insertLatexImageToCell(Workbook workbook, Sheet sheet, String latex, int rowIndex, int colIndex) throws IOException { |
| 144 | - // 设置固定30像素行高 | |
| 145 | - Row row = sheet.getRow(rowIndex); | |
| 146 | - if (row == null) { | |
| 147 | - row = sheet.createRow(rowIndex); | |
| 225 | + int fontSize = resolveCellFontSize(workbook, sheet, rowIndex, colIndex); | |
| 226 | + | |
| 227 | + CellRangeAddress mergedRegion = findMergedRegion(sheet, rowIndex, colIndex); | |
| 228 | + if (mergedRegion != null) { | |
| 229 | + insertLatexImageToRegion( | |
| 230 | + workbook, | |
| 231 | + sheet, | |
| 232 | + latex, | |
| 233 | + mergedRegion.getFirstRow(), | |
| 234 | + mergedRegion.getFirstColumn(), | |
| 235 | + mergedRegion.getLastRow() - mergedRegion.getFirstRow() + 1, | |
| 236 | + mergedRegion.getLastColumn() - mergedRegion.getFirstColumn() + 1, | |
| 237 | + fontSize, | |
| 238 | + null | |
| 239 | + ); | |
| 240 | + return; | |
| 148 | 241 | } |
| 149 | 242 | |
| 150 | - row.setHeightInPoints(22.5f); // 30像素 | |
| 243 | + insertLatexImageToRegion(workbook, sheet, latex, rowIndex, colIndex, 1, 1, fontSize, null); | |
| 244 | + } | |
| 151 | 245 | |
| 152 | - byte[] imageBytes = latexToImageBytes(latex, 48); | |
| 153 | - ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes); | |
| 154 | - BufferedImage image = ImageIO.read(bais); | |
| 155 | - bais.close(); | |
| 246 | + /** | |
| 247 | + * 将LaTeX公式作为图片插入到指定单元格 | |
| 248 | + */ | |
| 249 | + public static void insertLatexImageToCell(Workbook workbook, Sheet sheet, String latex, int rowIndex, int colIndex, int rowRange, int colRange) throws IOException { | |
| 250 | + insertLatexImageToRegion(workbook, sheet, latex, rowIndex, colIndex, rowRange, colRange, 16, null); | |
| 251 | + } | |
| 156 | 252 | |
| 157 | - // 如果图片高度超过30像素,按比例缩小 | |
| 158 | - double scale = 1.0; | |
| 159 | - if (image.getHeight() > 30) { | |
| 160 | - scale = 30.0 / image.getHeight(); | |
| 161 | - System.out.println("图片需要缩放: " + scale); | |
| 253 | + private static final int EMU_PER_PX = 9525; | |
| 254 | + | |
| 255 | + public static void insertLatexImageToCellWithMaxScale(Workbook workbook, Sheet sheet, String latex, int rowIndex, int colIndex, | |
| 256 | + double maxScale) throws IOException { | |
| 257 | + int fontSize = resolveCellFontSize(workbook, sheet, rowIndex, colIndex); | |
| 258 | + CellRangeAddress mergedRegion = findMergedRegion(sheet, rowIndex, colIndex); | |
| 259 | + if (mergedRegion != null) { | |
| 260 | + insertLatexImageToRegion( | |
| 261 | + workbook, | |
| 262 | + sheet, | |
| 263 | + latex, | |
| 264 | + mergedRegion.getFirstRow(), | |
| 265 | + mergedRegion.getFirstColumn(), | |
| 266 | + mergedRegion.getLastRow() - mergedRegion.getFirstRow() + 1, | |
| 267 | + mergedRegion.getLastColumn() - mergedRegion.getFirstColumn() + 1, | |
| 268 | + fontSize, | |
| 269 | + maxScale | |
| 270 | + ); | |
| 271 | + return; | |
| 162 | 272 | } |
| 273 | + insertLatexImageToRegion(workbook, sheet, latex, rowIndex, colIndex, 1, 1, fontSize, maxScale); | |
| 274 | + } | |
| 275 | + | |
| 276 | + public static double calculateFitScale(Workbook workbook, Sheet sheet, String latex, int rowIndex, int colIndex) throws IOException { | |
| 277 | + int fontSize = resolveCellFontSize(workbook, sheet, rowIndex, colIndex); | |
| 278 | + CellRangeAddress mergedRegion = findMergedRegion(sheet, rowIndex, colIndex); | |
| 279 | + if (mergedRegion != null) { | |
| 280 | + return calculateFitScaleForRegion( | |
| 281 | + sheet, | |
| 282 | + latex, | |
| 283 | + mergedRegion.getFirstRow(), | |
| 284 | + mergedRegion.getFirstColumn(), | |
| 285 | + mergedRegion.getLastRow() - mergedRegion.getFirstRow() + 1, | |
| 286 | + mergedRegion.getLastColumn() - mergedRegion.getFirstColumn() + 1, | |
| 287 | + fontSize | |
| 288 | + ); | |
| 289 | + } | |
| 290 | + return calculateFitScaleForRegion(sheet, latex, rowIndex, colIndex, 1, 1, fontSize); | |
| 291 | + } | |
| 292 | + | |
| 293 | + private static double calculateFitScaleForRegion(Sheet sheet, String latex, | |
| 294 | + int rowIndex, int colIndex, int rowRange, int colRange, | |
| 295 | + int fontSize) throws IOException { | |
| 296 | + byte[] imageBytes = latexToImageBytes(latex, fontSize); | |
| 297 | + BufferedImage image; | |
| 298 | + try (ByteArrayInputStream in = new ByteArrayInputStream(imageBytes)) { | |
| 299 | + image = ImageIO.read(in); | |
| 300 | + } | |
| 301 | + if (image == null) { | |
| 302 | + return 1.0; | |
| 303 | + } | |
| 304 | + | |
| 305 | + int regionWidthPx = 0; | |
| 306 | + for (int c = colIndex; c < colIndex + colRange; c++) { | |
| 307 | + regionWidthPx += getColumnWidthPx(sheet, c); | |
| 308 | + } | |
| 309 | + int regionHeightPx = 0; | |
| 310 | + for (int r = rowIndex; r < rowIndex + rowRange; r++) { | |
| 311 | + regionHeightPx += getRowHeightPx(sheet, r); | |
| 312 | + } | |
| 313 | + | |
| 314 | + int paddingXPx = 6; | |
| 315 | + int paddingYPx = 2; | |
| 316 | + double scale = Math.min( | |
| 317 | + (double) (regionWidthPx - paddingXPx * 2) / Math.max(1, image.getWidth()), | |
| 318 | + (double) (regionHeightPx - paddingYPx * 2) / Math.max(1, image.getHeight()) | |
| 319 | + ); | |
| 320 | + return Math.min(scale, 1.0); | |
| 321 | + } | |
| 322 | + | |
| 323 | + private static void insertLatexImageToRegion(Workbook workbook, Sheet sheet, String latex, | |
| 324 | + int rowIndex, int colIndex, int rowRange, int colRange, | |
| 325 | + int fontSize, Double maxScale) throws IOException { | |
| 326 | + byte[] imageBytes = latexToImageBytes(latex, fontSize); | |
| 327 | + BufferedImage image; | |
| 328 | + try (ByteArrayInputStream in = new ByteArrayInputStream(imageBytes)) { | |
| 329 | + image = ImageIO.read(in); | |
| 330 | + } | |
| 331 | + | |
| 332 | + if (image == null) { | |
| 333 | + return; | |
| 334 | + } | |
| 335 | + | |
| 336 | + int regionWidthPx = 0; | |
| 337 | + for (int c = colIndex; c < colIndex + colRange; c++) { | |
| 338 | + regionWidthPx += getColumnWidthPx(sheet, c); | |
| 339 | + } | |
| 340 | + int regionHeightPx = 0; | |
| 341 | + for (int r = rowIndex; r < rowIndex + rowRange; r++) { | |
| 342 | + regionHeightPx += getRowHeightPx(sheet, r); | |
| 343 | + } | |
| 344 | + | |
| 345 | + int paddingXPx = 6; | |
| 346 | + int paddingYPx = 2; | |
| 347 | + double scale = Math.min( | |
| 348 | + (double) (regionWidthPx - paddingXPx * 2) / Math.max(1, image.getWidth()), | |
| 349 | + (double) (regionHeightPx - paddingYPx * 2) / Math.max(1, image.getHeight()) | |
| 350 | + ); | |
| 351 | + scale = Math.min(scale, 1.0); | |
| 352 | + if (maxScale != null) { | |
| 353 | + scale = Math.min(scale, maxScale); | |
| 354 | + } | |
| 355 | + | |
| 356 | + int scaledWidthPx = Math.max(1, (int) Math.round(image.getWidth() * scale)); | |
| 357 | + int scaledHeightPx = Math.max(1, (int) Math.round(image.getHeight() * scale)); | |
| 358 | + | |
| 359 | + int offsetXPx = Math.max(paddingXPx, (regionWidthPx - scaledWidthPx) / 2); | |
| 360 | + int offsetYPx = Math.max(paddingYPx, (regionHeightPx - scaledHeightPx) / 2); | |
| 163 | 361 | |
| 164 | 362 | int pictureIdx = workbook.addPicture(imageBytes, Workbook.PICTURE_TYPE_PNG); |
| 165 | 363 | Drawing<?> drawing = sheet.createDrawingPatriarch(); |
| ... | ... | @@ -167,59 +365,142 @@ public class LatexFormulaExcelExporterUtil { |
| 167 | 365 | ClientAnchor anchor = workbook.getCreationHelper().createClientAnchor(); |
| 168 | 366 | anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_DONT_RESIZE); |
| 169 | 367 | |
| 170 | - anchor.setCol1(colIndex); | |
| 171 | - anchor.setRow1(rowIndex); | |
| 172 | - anchor.setCol2(colIndex); | |
| 173 | - anchor.setRow2(rowIndex); | |
| 368 | + AnchorCoord x1 = resolveAnchorX(sheet, colIndex, colRange, offsetXPx); | |
| 369 | + AnchorCoord x2 = resolveAnchorX(sheet, colIndex, colRange, offsetXPx + scaledWidthPx); | |
| 370 | + AnchorCoord y1 = resolveAnchorY(sheet, rowIndex, rowRange, offsetYPx); | |
| 371 | + AnchorCoord y2 = resolveAnchorY(sheet, rowIndex, rowRange, offsetYPx + scaledHeightPx); | |
| 174 | 372 | |
| 175 | - int scaledWidth = (int) (image.getWidth() * scale); | |
| 176 | - int rate = 1; | |
| 177 | - if (scaledWidth >= 60 && scaledWidth <= 150) { | |
| 178 | - rate = 2; | |
| 179 | - } else if (scaledWidth > 150) { | |
| 180 | - rate = 3; | |
| 181 | - } | |
| 373 | + anchor.setCol1(x1.index); | |
| 374 | + anchor.setCol2(x2.index); | |
| 375 | + anchor.setRow1(y1.index); | |
| 376 | + anchor.setRow2(y2.index); | |
| 182 | 377 | |
| 183 | - anchor.setDx1(25); | |
| 184 | - anchor.setDy1(25); | |
| 185 | - anchor.setDx2(330 * rate); | |
| 186 | - anchor.setDy2(235); | |
| 378 | + if (anchor instanceof org.apache.poi.hssf.usermodel.HSSFClientAnchor) { | |
| 379 | + int dx1 = pxToHssfDx(sheet, x1.index, x1.offsetPx); | |
| 380 | + int dx2 = pxToHssfDx(sheet, x2.index, x2.offsetPx); | |
| 381 | + int dy1 = pxToHssfDy(sheet, y1.index, y1.offsetPx); | |
| 382 | + int dy2 = pxToHssfDy(sheet, y2.index, y2.offsetPx); | |
| 383 | + | |
| 384 | + if (x2.index == x1.index && dx2 <= dx1) { | |
| 385 | + dx2 = Math.min(1023, dx1 + 1); | |
| 386 | + } | |
| 387 | + if (y2.index == y1.index && dy2 <= dy1) { | |
| 388 | + dy2 = Math.min(255, dy1 + 1); | |
| 389 | + } | |
| 390 | + | |
| 391 | + anchor.setDx1(dx1); | |
| 392 | + anchor.setDx2(dx2); | |
| 393 | + anchor.setDy1(dy1); | |
| 394 | + anchor.setDy2(dy2); | |
| 395 | + } else { | |
| 396 | + int dx1 = x1.offsetPx * EMU_PER_PX; | |
| 397 | + int dx2 = x2.offsetPx * EMU_PER_PX; | |
| 398 | + int dy1 = y1.offsetPx * EMU_PER_PX; | |
| 399 | + int dy2 = y2.offsetPx * EMU_PER_PX; | |
| 400 | + | |
| 401 | + if (x2.index == x1.index && dx2 <= dx1) { | |
| 402 | + dx2 = dx1 + 1; | |
| 403 | + } | |
| 404 | + if (y2.index == y1.index && dy2 <= dy1) { | |
| 405 | + dy2 = dy1 + 1; | |
| 406 | + } | |
| 407 | + | |
| 408 | + anchor.setDx1(dx1); | |
| 409 | + anchor.setDx2(dx2); | |
| 410 | + anchor.setDy1(dy1); | |
| 411 | + anchor.setDy2(dy2); | |
| 412 | + } | |
| 187 | 413 | |
| 188 | 414 | drawing.createPicture(anchor, pictureIdx); |
| 189 | 415 | } |
| 190 | 416 | |
| 191 | - /** | |
| 192 | - * 将LaTeX公式作为图片插入到指定单元格 | |
| 193 | - */ | |
| 194 | - public static void insertLatexImageToCell(Workbook workbook, Sheet sheet, String latex, int rowIndex, int colIndex, int rowRange, int colRange) throws IOException { | |
| 195 | - // 1. 将LaTeX渲染为图片字节 | |
| 196 | - byte[] imageBytes = latexToImageBytes(latex, 36); | |
| 417 | + private static int getColumnWidthPx(Sheet sheet, int colIndex) { | |
| 418 | + int widthUnits = sheet.getColumnWidth(colIndex); | |
| 419 | + return Math.max(1, (int) Math.round(widthUnits / 256.0 * 7.0 + 5)); | |
| 420 | + } | |
| 197 | 421 | |
| 198 | - // 2. 将图片添加到工作簿 | |
| 199 | - int pictureIdx = workbook.addPicture(imageBytes, Workbook.PICTURE_TYPE_PNG); | |
| 422 | + private static int getRowHeightPx(Sheet sheet, int rowIndex) { | |
| 423 | + Row row = sheet.getRow(rowIndex); | |
| 424 | + float points = row == null ? sheet.getDefaultRowHeightInPoints() : row.getHeightInPoints(); | |
| 425 | + return Math.max(1, (int) Math.round(points * 96.0 / 72.0)); | |
| 426 | + } | |
| 200 | 427 | |
| 201 | - // 3. 创建绘图对象 | |
| 202 | - Drawing<?> drawing = sheet.createDrawingPatriarch(); | |
| 428 | + private static class AnchorCoord { | |
| 429 | + final int index; | |
| 430 | + final int offsetPx; | |
| 203 | 431 | |
| 204 | - // 4. 创建锚点,确定图片位置 | |
| 205 | - ClientAnchor anchor = workbook.getCreationHelper().createClientAnchor(); | |
| 206 | - anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE); | |
| 207 | - anchor.setCol1(colIndex); // C列 (从0开始计数) | |
| 208 | - anchor.setRow1(rowIndex); // 当前数据行 (表头在第0行) | |
| 209 | - anchor.setCol2(colIndex + colRange); // 结束于D列 | |
| 210 | - anchor.setRow2(rowIndex + rowRange); // 结束于下一行 | |
| 432 | + private AnchorCoord(int index, int offsetPx) { | |
| 433 | + this.index = index; | |
| 434 | + this.offsetPx = offsetPx; | |
| 435 | + } | |
| 436 | + } | |
| 211 | 437 | |
| 212 | - // 设置较小的起始偏移 | |
| 213 | - anchor.setDx1(10000); // 水平偏移(建议值:10-100) | |
| 214 | - anchor.setDy1(10000); // 垂直偏移(建议值:10-50) | |
| 438 | + private static AnchorCoord resolveAnchorX(Sheet sheet, int colIndex, int colRange, int offsetPx) { | |
| 439 | + int remaining = offsetPx; | |
| 440 | + for (int c = colIndex; c < colIndex + colRange; c++) { | |
| 441 | + int w = getColumnWidthPx(sheet, c); | |
| 442 | + if (remaining <= w) { | |
| 443 | + return new AnchorCoord(c, Math.max(0, remaining)); | |
| 444 | + } | |
| 445 | + remaining -= w; | |
| 446 | + } | |
| 447 | + int lastCol = colIndex + colRange - 1; | |
| 448 | + return new AnchorCoord(lastCol, getColumnWidthPx(sheet, lastCol)); | |
| 449 | + } | |
| 450 | + | |
| 451 | + private static AnchorCoord resolveAnchorY(Sheet sheet, int rowIndex, int rowRange, int offsetPx) { | |
| 452 | + int remaining = offsetPx; | |
| 453 | + for (int r = rowIndex; r < rowIndex + rowRange; r++) { | |
| 454 | + int h = getRowHeightPx(sheet, r); | |
| 455 | + if (remaining <= h) { | |
| 456 | + return new AnchorCoord(r, Math.max(0, remaining)); | |
| 457 | + } | |
| 458 | + remaining -= h; | |
| 459 | + } | |
| 460 | + int lastRow = rowIndex + rowRange - 1; | |
| 461 | + return new AnchorCoord(lastRow, getRowHeightPx(sheet, lastRow)); | |
| 462 | + } | |
| 463 | + | |
| 464 | + private static int pxToHssfDx(Sheet sheet, int colIndex, int offsetPx) { | |
| 465 | + int w = getColumnWidthPx(sheet, colIndex); | |
| 466 | + int px = Math.min(Math.max(offsetPx, 0), w); | |
| 467 | + return (int) Math.round(px * 1023.0 / Math.max(1, w)); | |
| 468 | + } | |
| 215 | 469 | |
| 470 | + private static int pxToHssfDy(Sheet sheet, int rowIndex, int offsetPx) { | |
| 471 | + int h = getRowHeightPx(sheet, rowIndex); | |
| 472 | + int px = Math.min(Math.max(offsetPx, 0), h); | |
| 473 | + return (int) Math.round(px * 255.0 / Math.max(1, h)); | |
| 474 | + } | |
| 216 | 475 | |
| 217 | - // 5. 创建图片并插入 | |
| 218 | - Picture pict = drawing.createPicture(anchor, pictureIdx); | |
| 476 | + private static CellRangeAddress findMergedRegion(Sheet sheet, int rowIndex, int colIndex) { | |
| 477 | + for (int i = 0; i < sheet.getNumMergedRegions(); i++) { | |
| 478 | + CellRangeAddress region = sheet.getMergedRegion(i); | |
| 479 | + if (region == null) { | |
| 480 | + continue; | |
| 481 | + } | |
| 482 | + if (rowIndex >= region.getFirstRow() | |
| 483 | + && rowIndex <= region.getLastRow() | |
| 484 | + && colIndex >= region.getFirstColumn() | |
| 485 | + && colIndex <= region.getLastColumn()) { | |
| 486 | + return region; | |
| 487 | + } | |
| 488 | + } | |
| 489 | + return null; | |
| 490 | + } | |
| 219 | 491 | |
| 220 | - // 6. 自动调整图片大小以适应单元格 (可选) | |
| 221 | - // 取消注释此行使图片自动适应锚点区域 | |
| 222 | - pict.resize(0.95); // 缩小到90%,自然产生边距 | |
| 492 | + private static int resolveCellFontSize(Workbook workbook, Sheet sheet, int rowIndex, int colIndex) { | |
| 493 | + Row row = sheet.getRow(rowIndex); | |
| 494 | + Cell cell = row == null ? null : row.getCell(colIndex); | |
| 495 | + CellStyle style = cell == null ? null : cell.getCellStyle(); | |
| 496 | + short fontIndex = style == null ? 0 : style.getFontIndex(); | |
| 497 | + Font font = workbook.getFontAt(fontIndex); | |
| 498 | + short cellFontSize = font == null ? 11 : font.getFontHeightInPoints(); | |
| 499 | + | |
| 500 | + int latexFontSize = cellFontSize + 4; | |
| 501 | + latexFontSize = Math.max(14, latexFontSize); | |
| 502 | + latexFontSize = Math.min(20, latexFontSize); | |
| 503 | + return latexFontSize; | |
| 223 | 504 | } |
| 224 | 505 | |
| 225 | 506 | @Data | ... | ... |
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type