Commit 368334159750cc814662214d66d8e118d60d9e83
Merge branch 'master_after0506' into master_after0506_report02
Showing
7 changed files
with
490 additions
and
22 deletions
| @@ -1194,7 +1194,7 @@ public class ContractDistributorStandardController extends DefaultBaseController | @@ -1194,7 +1194,7 @@ public class ContractDistributorStandardController extends DefaultBaseController | ||
| 1194 | addFormulaComponent(formulaComponentList, line.getWidth(), line.getWidthTolPos(), line.getWidthTolNeg()); | 1194 | addFormulaComponent(formulaComponentList, line.getWidth(), line.getWidthTolPos(), line.getWidthTolNeg()); |
| 1195 | addFormulaComponent(formulaComponentList, line.getLength(), line.getLengthTolPos(), line.getLengthTolNeg()); | 1195 | addFormulaComponent(formulaComponentList, line.getLength(), line.getLengthTolPos(), line.getLengthTolNeg()); |
| 1196 | 1196 | ||
| 1197 | - String latex = LatexFormulaExcelExporterUtil.convertToContractSpecLatexSingleLineFixed3(formulaComponentList); | 1197 | + String latex = LatexFormulaExcelExporterUtil.convertToContractSpecLatexSingleLineFixed3Compact(formulaComponentList); |
| 1198 | if (StringUtils.isNotBlank(latex)) { | 1198 | if (StringUtils.isNotBlank(latex)) { |
| 1199 | specImageCells.add(new SpecImageCell(startRow, 5, latex)); | 1199 | specImageCells.add(new SpecImageCell(startRow, 5, latex)); |
| 1200 | } | 1200 | } |
| @@ -1238,7 +1238,7 @@ public class ContractDistributorStandardController extends DefaultBaseController | @@ -1238,7 +1238,7 @@ public class ContractDistributorStandardController extends DefaultBaseController | ||
| 1238 | addFormulaComponent(formulaComponentList, line.getWidth(), line.getWidthTolPos(), line.getWidthTolNeg()); | 1238 | addFormulaComponent(formulaComponentList, line.getWidth(), line.getWidthTolPos(), line.getWidthTolNeg()); |
| 1239 | addFormulaComponent(formulaComponentList, line.getLength(), line.getLengthTolPos(), line.getLengthTolNeg()); | 1239 | addFormulaComponent(formulaComponentList, line.getLength(), line.getLengthTolPos(), line.getLengthTolNeg()); |
| 1240 | 1240 | ||
| 1241 | - String latex = LatexFormulaExcelExporterUtil.convertToContractSpecLatexSingleLineFixed3(formulaComponentList); | 1241 | + String latex = LatexFormulaExcelExporterUtil.convertToContractSpecLatexSingleLineFixed3Compact(formulaComponentList); |
| 1242 | if (StringUtils.isNotBlank(latex)) { | 1242 | if (StringUtils.isNotBlank(latex)) { |
| 1243 | specImageCells.add(new SpecImageCell(startRow, 9, latex)); | 1243 | specImageCells.add(new SpecImageCell(startRow, 9, latex)); |
| 1244 | } | 1244 | } |
| @@ -1253,13 +1253,42 @@ public class ContractDistributorStandardController extends DefaultBaseController | @@ -1253,13 +1253,42 @@ public class ContractDistributorStandardController extends DefaultBaseController | ||
| 1253 | } | 1253 | } |
| 1254 | } | 1254 | } |
| 1255 | 1255 | ||
| 1256 | - double specMinScale = 1.0; | 1256 | + int contractSpecFontSize = Integer.MAX_VALUE; |
| 1257 | for (SpecImageCell specCell : specImageCells) { | 1257 | for (SpecImageCell specCell : specImageCells) { |
| 1258 | - double scale = LatexFormulaExcelExporterUtil.calculateFitScale(workbook, sheet, specCell.latex, specCell.rowIndex, specCell.colIndex); | ||
| 1259 | - specMinScale = Math.min(specMinScale, scale); | 1258 | + int currentFontSize = LatexFormulaExcelExporterUtil.calculateContractReferenceFontSize( |
| 1259 | + workbook, | ||
| 1260 | + sheet, | ||
| 1261 | + specCell.rowIndex, | ||
| 1262 | + specCell.colIndex | ||
| 1263 | + ); | ||
| 1264 | + contractSpecFontSize = Math.min(contractSpecFontSize, currentFontSize); | ||
| 1260 | } | 1265 | } |
| 1266 | + if (contractSpecFontSize == Integer.MAX_VALUE) { | ||
| 1267 | + contractSpecFontSize = 16; | ||
| 1268 | + } | ||
| 1269 | + | ||
| 1270 | + double contractSpecScale = 1.0; | ||
| 1271 | + for (SpecImageCell specCell : specImageCells) { | ||
| 1272 | + double currentScale = LatexFormulaExcelExporterUtil.calculateContractReferenceFitScale( | ||
| 1273 | + workbook, | ||
| 1274 | + sheet, | ||
| 1275 | + specCell.rowIndex, | ||
| 1276 | + specCell.colIndex, | ||
| 1277 | + contractSpecFontSize | ||
| 1278 | + ); | ||
| 1279 | + contractSpecScale = Math.min(contractSpecScale, currentScale); | ||
| 1280 | + } | ||
| 1281 | + | ||
| 1261 | for (SpecImageCell specCell : specImageCells) { | 1282 | for (SpecImageCell specCell : specImageCells) { |
| 1262 | - LatexFormulaExcelExporterUtil.insertLatexImageToCellWithMaxScale(workbook, sheet, specCell.latex, specCell.rowIndex, specCell.colIndex, specMinScale); | 1283 | + LatexFormulaExcelExporterUtil.insertContractLatexImageToCellFillTemplate( |
| 1284 | + workbook, | ||
| 1285 | + sheet, | ||
| 1286 | + specCell.latex, | ||
| 1287 | + specCell.rowIndex, | ||
| 1288 | + specCell.colIndex, | ||
| 1289 | + contractSpecFontSize, | ||
| 1290 | + contractSpecScale | ||
| 1291 | + ); | ||
| 1263 | } | 1292 | } |
| 1264 | 1293 | ||
| 1265 | // --- 填充全局变量 --- | 1294 | // --- 填充全局变量 --- |
xingyun-sc/src/main/java/com/lframework/xingyun/sc/controller/order/PurchaseOrderInfoController.java
| @@ -444,7 +444,7 @@ public class PurchaseOrderInfoController extends DefaultBaseController { | @@ -444,7 +444,7 @@ public class PurchaseOrderInfoController extends DefaultBaseController { | ||
| 444 | dataMap.put("supplyUnit", supplyUnitDicItem == null ? "" : supplyUnitDicItem.getName()); | 444 | dataMap.put("supplyUnit", supplyUnitDicItem == null ? "" : supplyUnitDicItem.getName()); |
| 445 | dataMap.put("orderNo", data.getOrderNo()); | 445 | dataMap.put("orderNo", data.getOrderNo()); |
| 446 | dataMap.put("orderingUnitName", data.getOrderingUnitName()); | 446 | dataMap.put("orderingUnitName", data.getOrderingUnitName()); |
| 447 | - dataMap.put("workshopName", data.getWorkshopName()); | 447 | + dataMap.put("workshopName", data.getWorkshopName() == null ? "" : data.getWorkshopName()); |
| 448 | dataMap.put("customerTier", data.getCustomerTier()); | 448 | dataMap.put("customerTier", data.getCustomerTier()); |
| 449 | dataMap.put("orderDate", data.getOrderDate() == null ? "" : data.getOrderDate().format(dateFormatter)); | 449 | dataMap.put("orderDate", data.getOrderDate() == null ? "" : data.getOrderDate().format(dateFormatter)); |
| 450 | 450 |
| @@ -243,6 +243,7 @@ public class BusinessDataExportHandler implements ExportHandler { | @@ -243,6 +243,7 @@ public class BusinessDataExportHandler implements ExportHandler { | ||
| 243 | SysDataDicItem supplyUnitDicItem = sysDataDicItemService.findByCode("SUPPLIER", orderInfo.getSupplyUnit()); | 243 | SysDataDicItem supplyUnitDicItem = sysDataDicItemService.findByCode("SUPPLIER", orderInfo.getSupplyUnit()); |
| 244 | dataMap.put("supplyUnit", supplyUnitDicItem == null ? "" : supplyUnitDicItem.getName()); | 244 | dataMap.put("supplyUnit", supplyUnitDicItem == null ? "" : supplyUnitDicItem.getName()); |
| 245 | dataMap.put("orderNo", orderInfo.getOrderNo()); | 245 | dataMap.put("orderNo", orderInfo.getOrderNo()); |
| 246 | + dataMap.put("workshopName", orderInfo.getWorkshopName() == null ? "" : orderInfo.getWorkshopName()); | ||
| 246 | dataMap.put("orderingUnitName", orderInfo.getOrderingUnitName()); | 247 | dataMap.put("orderingUnitName", orderInfo.getOrderingUnitName()); |
| 247 | dataMap.put("customerTier", orderInfo.getCustomerTier()); | 248 | dataMap.put("customerTier", orderInfo.getCustomerTier()); |
| 248 | dataMap.put("orderDate", orderInfo.getOrderDate() == null ? "" : orderInfo.getOrderDate().format(dateFormatter)); | 249 | dataMap.put("orderDate", orderInfo.getOrderDate() == null ? "" : orderInfo.getOrderDate().format(dateFormatter)); |
| @@ -288,7 +289,8 @@ public class BusinessDataExportHandler implements ExportHandler { | @@ -288,7 +289,8 @@ public class BusinessDataExportHandler implements ExportHandler { | ||
| 288 | } | 289 | } |
| 289 | File pdfFile = ExcelUtil.convertExcelToPdf(tempExcel, "/usr/bin/libreoffice --headless --convert-to pdf --outdir %s %s"); | 290 | File pdfFile = ExcelUtil.convertExcelToPdf(tempExcel, "/usr/bin/libreoffice --headless --convert-to pdf --outdir %s %s"); |
| 290 | try (InputStream pdfIn = new FileInputStream(pdfFile)) { | 291 | try (InputStream pdfIn = new FileInputStream(pdfFile)) { |
| 291 | - ZipEntry entry = new ZipEntry(orderInfo.getOrderNo() + "-订货单打印-" + createTime + ".pdf"); | 292 | + ZipEntry entry = new ZipEntry(orderInfo.getOrderNo() + "-" |
| 293 | + + orderInfo.getOrderingUnitName() + "-" + orderInfo.getWorkshopName() + "-订货单打印" + ".pdf"); | ||
| 292 | zos.putNextEntry(entry); | 294 | zos.putNextEntry(entry); |
| 293 | byte[] buffer = new byte[8192]; | 295 | byte[] buffer = new byte[8192]; |
| 294 | int len; | 296 | int len; |
| @@ -307,7 +309,8 @@ public class BusinessDataExportHandler implements ExportHandler { | @@ -307,7 +309,8 @@ public class BusinessDataExportHandler implements ExportHandler { | ||
| 307 | } else { | 309 | } else { |
| 308 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); | 310 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| 309 | workbook.write(baos); | 311 | workbook.write(baos); |
| 310 | - ZipEntry entry = new ZipEntry(orderInfo.getOrderNo() + "-订货单打印-" + createTime + ".xlsx"); | 312 | + ZipEntry entry = new ZipEntry(orderInfo.getOrderNo() + "-" |
| 313 | + + orderInfo.getOrderingUnitName() + "-" + orderInfo.getWorkshopName() + "-订货单打印" + ".xlsx"); | ||
| 311 | zos.putNextEntry(entry); | 314 | zos.putNextEntry(entry); |
| 312 | zos.write(baos.toByteArray()); | 315 | zos.write(baos.toByteArray()); |
| 313 | zos.closeEntry(); | 316 | zos.closeEntry(); |
| @@ -24,6 +24,9 @@ import java.util.List; | @@ -24,6 +24,9 @@ import java.util.List; | ||
| 24 | @Slf4j | 24 | @Slf4j |
| 25 | public class LatexFormulaExcelExporterUtil { | 25 | public class LatexFormulaExcelExporterUtil { |
| 26 | 26 | ||
| 27 | + private static final String CONTRACT_SPEC_REFERENCE_LATEX = | ||
| 28 | + "\\mathbf{\\mathrm{22^{+0.2}_{+0.1}\\!\\times\\!22^{+0.2}_{+0.1}\\!\\times\\!22^{+0.2}_{+0.1}}}"; | ||
| 29 | + | ||
| 27 | public static String convertToLatex(List<FormulaComponent> componentList) { | 30 | public static String convertToLatex(List<FormulaComponent> componentList) { |
| 28 | if (CollectionUtils.isEmpty(componentList)) { | 31 | if (CollectionUtils.isEmpty(componentList)) { |
| 29 | return ""; | 32 | return ""; |
| @@ -97,11 +100,45 @@ public class LatexFormulaExcelExporterUtil { | @@ -97,11 +100,45 @@ public class LatexFormulaExcelExporterUtil { | ||
| 97 | return ""; | 100 | return ""; |
| 98 | } | 101 | } |
| 99 | 102 | ||
| 103 | + String referenceComponentLatex = resolveReferenceComponentLatex(componentList); | ||
| 100 | int missing = Math.max(0, 3 - nonNullCount); | 104 | int missing = Math.max(0, 3 - nonNullCount); |
| 101 | if (missing > 0) { | 105 | if (missing > 0) { |
| 102 | StringBuilder phantom = new StringBuilder(); | 106 | StringBuilder phantom = new StringBuilder(); |
| 103 | for (int i = 0; i < missing; i++) { | 107 | for (int i = 0; i < missing; i++) { |
| 104 | - phantom.append(" \\times 000^{+00}_{+00}"); | 108 | + phantom.append(" \\times ").append(referenceComponentLatex); |
| 109 | + } | ||
| 110 | + actual = actual + "\\phantom{" + phantom + "}"; | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + return "\\mathbf{\\mathrm{" + actual + "}}"; | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + public static String convertToContractSpecLatexSingleLineFixed3Compact(List<FormulaComponent> componentList) { | ||
| 117 | + if (CollectionUtils.isEmpty(componentList)) { | ||
| 118 | + return ""; | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + int nonNullCount = 0; | ||
| 122 | + for (FormulaComponent comp : componentList) { | ||
| 123 | + if (comp != null && comp.getBase() != null) { | ||
| 124 | + nonNullCount++; | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | + if (nonNullCount <= 0) { | ||
| 128 | + return ""; | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + String actual = buildLatexLine(componentList, 0, componentList.size(), "\\!\\times\\!"); | ||
| 132 | + if (actual.isEmpty()) { | ||
| 133 | + return ""; | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + String referenceComponentLatex = resolveReferenceComponentLatex(componentList); | ||
| 137 | + int missing = Math.max(0, 3 - nonNullCount); | ||
| 138 | + if (missing > 0) { | ||
| 139 | + StringBuilder phantom = new StringBuilder(); | ||
| 140 | + for (int i = 0; i < missing; i++) { | ||
| 141 | + phantom.append("\\!\\times\\!").append(referenceComponentLatex); | ||
| 105 | } | 142 | } |
| 106 | actual = actual + "\\phantom{" + phantom + "}"; | 143 | actual = actual + "\\phantom{" + phantom + "}"; |
| 107 | } | 144 | } |
| @@ -110,6 +147,10 @@ public class LatexFormulaExcelExporterUtil { | @@ -110,6 +147,10 @@ public class LatexFormulaExcelExporterUtil { | ||
| 110 | } | 147 | } |
| 111 | 148 | ||
| 112 | private static String buildLatexLine(List<FormulaComponent> componentList, int startIndex, int endIndexExclusive) { | 149 | private static String buildLatexLine(List<FormulaComponent> componentList, int startIndex, int endIndexExclusive) { |
| 150 | + return buildLatexLine(componentList, startIndex, endIndexExclusive, " \\times "); | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + private static String buildLatexLine(List<FormulaComponent> componentList, int startIndex, int endIndexExclusive, String separator) { | ||
| 113 | StringBuilder latex = new StringBuilder(); | 154 | StringBuilder latex = new StringBuilder(); |
| 114 | boolean first = true; | 155 | boolean first = true; |
| 115 | int end = Math.min(componentList.size(), endIndexExclusive); | 156 | int end = Math.min(componentList.size(), endIndexExclusive); |
| @@ -119,24 +160,48 @@ public class LatexFormulaExcelExporterUtil { | @@ -119,24 +160,48 @@ public class LatexFormulaExcelExporterUtil { | ||
| 119 | continue; | 160 | continue; |
| 120 | } | 161 | } |
| 121 | if (!first) { | 162 | if (!first) { |
| 122 | - latex.append(" \\times "); | 163 | + latex.append(separator); |
| 123 | } | 164 | } |
| 124 | first = false; | 165 | first = false; |
| 166 | + latex.append(buildLatexComponent(comp)); | ||
| 167 | + } | ||
| 168 | + return latex.toString(); | ||
| 169 | + } | ||
| 125 | 170 | ||
| 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("}"); | 171 | + private static String resolveReferenceComponentLatex(List<FormulaComponent> componentList) { |
| 172 | + String reference = "000^{+00}_{+00}"; | ||
| 173 | + int maxLength = reference.length(); | ||
| 174 | + for (FormulaComponent comp : componentList) { | ||
| 175 | + if (comp == null || comp.getBase() == null) { | ||
| 176 | + continue; | ||
| 129 | } | 177 | } |
| 130 | - if (comp.getSup() != null && comp.getSup().compareTo(BigDecimal.ZERO) < 0) { | ||
| 131 | - latex.append("^{").append(formatScientificNotation(comp.getSup())).append("}"); | 178 | + String candidate = buildLatexComponent(comp); |
| 179 | + if (candidate.length() > maxLength) { | ||
| 180 | + reference = candidate; | ||
| 181 | + maxLength = candidate.length(); | ||
| 132 | } | 182 | } |
| 183 | + } | ||
| 184 | + return reference; | ||
| 185 | + } | ||
| 133 | 186 | ||
| 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 | - } | 187 | + private static String buildLatexComponent(FormulaComponent comp) { |
| 188 | + if (comp == null || comp.getBase() == null) { | ||
| 189 | + return ""; | ||
| 190 | + } | ||
| 191 | + StringBuilder latex = new StringBuilder(); | ||
| 192 | + latex.append(formatScientificNotation(comp.getBase())); | ||
| 193 | + if (comp.getSup() != null && comp.getSup().compareTo(BigDecimal.ZERO) >= 0) { | ||
| 194 | + latex.append("^{+").append(formatScientificNotation(comp.getSup())).append("}"); | ||
| 195 | + } | ||
| 196 | + if (comp.getSup() != null && comp.getSup().compareTo(BigDecimal.ZERO) < 0) { | ||
| 197 | + latex.append("^{").append(formatScientificNotation(comp.getSup())).append("}"); | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + if (comp.getSub() != null && comp.getSub().compareTo(BigDecimal.ZERO) >= 0) { | ||
| 201 | + latex.append("_{+").append(formatScientificNotation(comp.getSub())).append("}"); | ||
| 202 | + } | ||
| 203 | + if (comp.getSub() != null && comp.getSub().compareTo(BigDecimal.ZERO) < 0) { | ||
| 204 | + latex.append("_{").append(formatScientificNotation(comp.getSub())).append("}"); | ||
| 140 | } | 205 | } |
| 141 | return latex.toString(); | 206 | return latex.toString(); |
| 142 | } | 207 | } |
| @@ -253,6 +318,86 @@ public class LatexFormulaExcelExporterUtil { | @@ -253,6 +318,86 @@ public class LatexFormulaExcelExporterUtil { | ||
| 253 | insertLatexImageToRegion(workbook, sheet, latex, rowIndex, colIndex, rowRange, colRange, 16, null); | 318 | insertLatexImageToRegion(workbook, sheet, latex, rowIndex, colIndex, rowRange, colRange, 16, null); |
| 254 | } | 319 | } |
| 255 | 320 | ||
| 321 | + /** | ||
| 322 | + * 合同规格专用:按照模板单元格区域高度优先铺满,并根据区域大小动态选择更合适的渲染字号。 | ||
| 323 | + */ | ||
| 324 | + public static void insertContractLatexImageToCellFillTemplate(Workbook workbook, Sheet sheet, String latex, | ||
| 325 | + int rowIndex, int colIndex) throws IOException { | ||
| 326 | + int baseFontSize = Math.max(16, resolveCellFontSize(workbook, sheet, rowIndex, colIndex)); | ||
| 327 | + insertContractLatexImageToCellFillTemplate(workbook, sheet, latex, rowIndex, colIndex, baseFontSize); | ||
| 328 | + } | ||
| 329 | + | ||
| 330 | + public static void insertContractLatexImageToCellFillTemplate(Workbook workbook, Sheet sheet, String latex, | ||
| 331 | + int rowIndex, int colIndex, int baseFontSize) throws IOException { | ||
| 332 | + insertContractLatexImageToCellFillTemplate(workbook, sheet, latex, rowIndex, colIndex, baseFontSize, null); | ||
| 333 | + } | ||
| 334 | + | ||
| 335 | + public static void insertContractLatexImageToCellFillTemplate(Workbook workbook, Sheet sheet, String latex, | ||
| 336 | + int rowIndex, int colIndex, int baseFontSize, | ||
| 337 | + Double maxScale) throws IOException { | ||
| 338 | + CellRangeAddress mergedRegion = findMergedRegion(sheet, rowIndex, colIndex); | ||
| 339 | + if (mergedRegion != null) { | ||
| 340 | + insertContractLatexImageToRegionFillTemplate( | ||
| 341 | + workbook, | ||
| 342 | + sheet, | ||
| 343 | + latex, | ||
| 344 | + mergedRegion.getFirstRow(), | ||
| 345 | + mergedRegion.getFirstColumn(), | ||
| 346 | + mergedRegion.getLastRow() - mergedRegion.getFirstRow() + 1, | ||
| 347 | + mergedRegion.getLastColumn() - mergedRegion.getFirstColumn() + 1, | ||
| 348 | + baseFontSize, | ||
| 349 | + maxScale | ||
| 350 | + ); | ||
| 351 | + return; | ||
| 352 | + } | ||
| 353 | + insertContractLatexImageToRegionFillTemplate(workbook, sheet, latex, rowIndex, colIndex, 1, 1, baseFontSize, maxScale); | ||
| 354 | + } | ||
| 355 | + | ||
| 356 | + public static int calculateContractBestFontSize(Workbook workbook, Sheet sheet, String latex, | ||
| 357 | + int rowIndex, int colIndex) throws IOException { | ||
| 358 | + int baseFontSize = Math.max(16, resolveCellFontSize(workbook, sheet, rowIndex, colIndex)); | ||
| 359 | + CellRangeAddress mergedRegion = findMergedRegion(sheet, rowIndex, colIndex); | ||
| 360 | + if (mergedRegion != null) { | ||
| 361 | + return calculateContractBestFontSizeForRegion( | ||
| 362 | + sheet, | ||
| 363 | + latex, | ||
| 364 | + mergedRegion.getFirstRow(), | ||
| 365 | + mergedRegion.getFirstColumn(), | ||
| 366 | + mergedRegion.getLastRow() - mergedRegion.getFirstRow() + 1, | ||
| 367 | + mergedRegion.getLastColumn() - mergedRegion.getFirstColumn() + 1, | ||
| 368 | + baseFontSize | ||
| 369 | + ); | ||
| 370 | + } | ||
| 371 | + return calculateContractBestFontSizeForRegion(sheet, latex, rowIndex, colIndex, 1, 1, baseFontSize); | ||
| 372 | + } | ||
| 373 | + | ||
| 374 | + public static int calculateContractReferenceFontSize(Workbook workbook, Sheet sheet, | ||
| 375 | + int rowIndex, int colIndex) throws IOException { | ||
| 376 | + return calculateContractBestFontSize(workbook, sheet, CONTRACT_SPEC_REFERENCE_LATEX, rowIndex, colIndex); | ||
| 377 | + } | ||
| 378 | + | ||
| 379 | + public static double calculateContractFitScale(Workbook workbook, Sheet sheet, String latex, | ||
| 380 | + int rowIndex, int colIndex, int fontSize) throws IOException { | ||
| 381 | + CellRangeAddress mergedRegion = findMergedRegion(sheet, rowIndex, colIndex); | ||
| 382 | + if (mergedRegion != null) { | ||
| 383 | + return calculateContractFitScaleForRegion( | ||
| 384 | + sheet, | ||
| 385 | + latex, | ||
| 386 | + mergedRegion.getFirstRow(), | ||
| 387 | + mergedRegion.getFirstColumn(), | ||
| 388 | + mergedRegion.getLastRow() - mergedRegion.getFirstRow() + 1, | ||
| 389 | + mergedRegion.getLastColumn() - mergedRegion.getFirstColumn() + 1, | ||
| 390 | + fontSize | ||
| 391 | + ); | ||
| 392 | + } | ||
| 393 | + return calculateContractFitScaleForRegion(sheet, latex, rowIndex, colIndex, 1, 1, fontSize); | ||
| 394 | + } | ||
| 395 | + | ||
| 396 | + public static double calculateContractReferenceFitScale(Workbook workbook, Sheet sheet, | ||
| 397 | + int rowIndex, int colIndex, int fontSize) throws IOException { | ||
| 398 | + return calculateContractFitScale(workbook, sheet, CONTRACT_SPEC_REFERENCE_LATEX, rowIndex, colIndex, fontSize); | ||
| 399 | + } | ||
| 400 | + | ||
| 256 | private static final int EMU_PER_PX = 9525; | 401 | private static final int EMU_PER_PX = 9525; |
| 257 | 402 | ||
| 258 | public static void insertLatexImageToCellWithMaxScale(Workbook workbook, Sheet sheet, String latex, int rowIndex, int colIndex, | 403 | public static void insertLatexImageToCellWithMaxScale(Workbook workbook, Sheet sheet, String latex, int rowIndex, int colIndex, |
| @@ -417,6 +562,148 @@ public class LatexFormulaExcelExporterUtil { | @@ -417,6 +562,148 @@ public class LatexFormulaExcelExporterUtil { | ||
| 417 | drawing.createPicture(anchor, pictureIdx); | 562 | drawing.createPicture(anchor, pictureIdx); |
| 418 | } | 563 | } |
| 419 | 564 | ||
| 565 | + private static void insertContractLatexImageToRegionFillTemplate(Workbook workbook, Sheet sheet, String latex, | ||
| 566 | + int rowIndex, int colIndex, int rowRange, int colRange, | ||
| 567 | + int baseFontSize, Double maxScale) throws IOException { | ||
| 568 | + int regionWidthPx = 0; | ||
| 569 | + for (int c = colIndex; c < colIndex + colRange; c++) { | ||
| 570 | + regionWidthPx += getColumnWidthPx(sheet, c); | ||
| 571 | + } | ||
| 572 | + int regionHeightPx = 0; | ||
| 573 | + for (int r = rowIndex; r < rowIndex + rowRange; r++) { | ||
| 574 | + regionHeightPx += getRowHeightPx(sheet, r); | ||
| 575 | + } | ||
| 576 | + | ||
| 577 | + int paddingXPx = 6; | ||
| 578 | + int paddingYPx = 4; | ||
| 579 | + int availableWidthPx = Math.max(1, regionWidthPx - paddingXPx * 2); | ||
| 580 | + int availableHeightPx = Math.max(1, regionHeightPx - paddingYPx * 2); | ||
| 581 | + RenderedLatexImage renderedImage = renderLatexImage(latex, baseFontSize); | ||
| 582 | + if (renderedImage.image == null) { | ||
| 583 | + return; | ||
| 584 | + } | ||
| 585 | + | ||
| 586 | + byte[] imageBytes = renderedImage.imageBytes; | ||
| 587 | + BufferedImage image = renderedImage.image; | ||
| 588 | + double scale = Math.min( | ||
| 589 | + 1.0, | ||
| 590 | + Math.min( | ||
| 591 | + (double) availableWidthPx / Math.max(1, image.getWidth()), | ||
| 592 | + (double) availableHeightPx / Math.max(1, image.getHeight()) | ||
| 593 | + ) | ||
| 594 | + ); | ||
| 595 | + if (maxScale != null) { | ||
| 596 | + scale = Math.min(scale, maxScale); | ||
| 597 | + } | ||
| 598 | + int scaledWidthPx = Math.max(1, (int) Math.round(image.getWidth() * scale)); | ||
| 599 | + int scaledHeightPx = Math.max(1, (int) Math.round(image.getHeight() * scale)); | ||
| 600 | + | ||
| 601 | + int offsetXPx = Math.max(paddingXPx, (regionWidthPx - scaledWidthPx) / 2); | ||
| 602 | + int offsetYPx = Math.max(paddingYPx, (regionHeightPx - scaledHeightPx) / 2); | ||
| 603 | + | ||
| 604 | + int pictureIdx = workbook.addPicture(imageBytes, Workbook.PICTURE_TYPE_PNG); | ||
| 605 | + Drawing<?> drawing = sheet.createDrawingPatriarch(); | ||
| 606 | + ClientAnchor anchor = workbook.getCreationHelper().createClientAnchor(); | ||
| 607 | + anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_DONT_RESIZE); | ||
| 608 | + | ||
| 609 | + AnchorCoord x1 = resolveAnchorX(sheet, colIndex, colRange, offsetXPx); | ||
| 610 | + AnchorCoord x2 = resolveAnchorX(sheet, colIndex, colRange, offsetXPx + scaledWidthPx); | ||
| 611 | + AnchorCoord y1 = resolveAnchorY(sheet, rowIndex, rowRange, offsetYPx); | ||
| 612 | + AnchorCoord y2 = resolveAnchorY(sheet, rowIndex, rowRange, offsetYPx + scaledHeightPx); | ||
| 613 | + | ||
| 614 | + anchor.setCol1(x1.index); | ||
| 615 | + anchor.setCol2(x2.index); | ||
| 616 | + anchor.setRow1(y1.index); | ||
| 617 | + anchor.setRow2(y2.index); | ||
| 618 | + | ||
| 619 | + if (anchor instanceof org.apache.poi.hssf.usermodel.HSSFClientAnchor) { | ||
| 620 | + int dx1 = pxToHssfDx(sheet, x1.index, x1.offsetPx); | ||
| 621 | + int dx2 = pxToHssfDx(sheet, x2.index, x2.offsetPx); | ||
| 622 | + int dy1 = pxToHssfDy(sheet, y1.index, y1.offsetPx); | ||
| 623 | + int dy2 = pxToHssfDy(sheet, y2.index, y2.offsetPx); | ||
| 624 | + | ||
| 625 | + if (x2.index == x1.index && dx2 <= dx1) { | ||
| 626 | + dx2 = Math.min(1023, dx1 + 1); | ||
| 627 | + } | ||
| 628 | + if (y2.index == y1.index && dy2 <= dy1) { | ||
| 629 | + dy2 = Math.min(255, dy1 + 1); | ||
| 630 | + } | ||
| 631 | + | ||
| 632 | + anchor.setDx1(dx1); | ||
| 633 | + anchor.setDx2(dx2); | ||
| 634 | + anchor.setDy1(dy1); | ||
| 635 | + anchor.setDy2(dy2); | ||
| 636 | + } else { | ||
| 637 | + int dx1 = x1.offsetPx * EMU_PER_PX; | ||
| 638 | + int dx2 = x2.offsetPx * EMU_PER_PX; | ||
| 639 | + int dy1 = y1.offsetPx * EMU_PER_PX; | ||
| 640 | + int dy2 = y2.offsetPx * EMU_PER_PX; | ||
| 641 | + | ||
| 642 | + if (x2.index == x1.index && dx2 <= dx1) { | ||
| 643 | + dx2 = dx1 + 1; | ||
| 644 | + } | ||
| 645 | + if (y2.index == y1.index && dy2 <= dy1) { | ||
| 646 | + dy2 = dy1 + 1; | ||
| 647 | + } | ||
| 648 | + | ||
| 649 | + anchor.setDx1(dx1); | ||
| 650 | + anchor.setDx2(dx2); | ||
| 651 | + anchor.setDy1(dy1); | ||
| 652 | + anchor.setDy2(dy2); | ||
| 653 | + } | ||
| 654 | + | ||
| 655 | + drawing.createPicture(anchor, pictureIdx); | ||
| 656 | + } | ||
| 657 | + | ||
| 658 | + private static int calculateContractBestFontSizeForRegion(Sheet sheet, String latex, | ||
| 659 | + int rowIndex, int colIndex, int rowRange, int colRange, | ||
| 660 | + int preferredFontSize) throws IOException { | ||
| 661 | + int regionWidthPx = 0; | ||
| 662 | + for (int c = colIndex; c < colIndex + colRange; c++) { | ||
| 663 | + regionWidthPx += getColumnWidthPx(sheet, c); | ||
| 664 | + } | ||
| 665 | + int regionHeightPx = 0; | ||
| 666 | + for (int r = rowIndex; r < rowIndex + rowRange; r++) { | ||
| 667 | + regionHeightPx += getRowHeightPx(sheet, r); | ||
| 668 | + } | ||
| 669 | + | ||
| 670 | + int paddingXPx = 3; | ||
| 671 | + int paddingYPx = 1; | ||
| 672 | + int availableWidthPx = Math.max(1, regionWidthPx - paddingXPx * 2); | ||
| 673 | + int availableHeightPx = Math.max(1, regionHeightPx - paddingYPx * 2); | ||
| 674 | + return renderBestFitLatexFontSize(latex, preferredFontSize, availableWidthPx, availableHeightPx); | ||
| 675 | + } | ||
| 676 | + | ||
| 677 | + private static double calculateContractFitScaleForRegion(Sheet sheet, String latex, | ||
| 678 | + int rowIndex, int colIndex, int rowRange, int colRange, | ||
| 679 | + int fontSize) throws IOException { | ||
| 680 | + int regionWidthPx = 0; | ||
| 681 | + for (int c = colIndex; c < colIndex + colRange; c++) { | ||
| 682 | + regionWidthPx += getColumnWidthPx(sheet, c); | ||
| 683 | + } | ||
| 684 | + int regionHeightPx = 0; | ||
| 685 | + for (int r = rowIndex; r < rowIndex + rowRange; r++) { | ||
| 686 | + regionHeightPx += getRowHeightPx(sheet, r); | ||
| 687 | + } | ||
| 688 | + | ||
| 689 | + int paddingXPx = 6; | ||
| 690 | + int paddingYPx = 4; | ||
| 691 | + int availableWidthPx = Math.max(1, regionWidthPx - paddingXPx * 2); | ||
| 692 | + int availableHeightPx = Math.max(1, regionHeightPx - paddingYPx * 2); | ||
| 693 | + RenderedLatexImage renderedImage = renderLatexImage(latex, fontSize); | ||
| 694 | + if (renderedImage.image == null) { | ||
| 695 | + return 1.0; | ||
| 696 | + } | ||
| 697 | + | ||
| 698 | + return Math.min( | ||
| 699 | + 1.0, | ||
| 700 | + Math.min( | ||
| 701 | + (double) availableWidthPx / Math.max(1, renderedImage.image.getWidth()), | ||
| 702 | + (double) availableHeightPx / Math.max(1, renderedImage.image.getHeight()) | ||
| 703 | + ) | ||
| 704 | + ); | ||
| 705 | + } | ||
| 706 | + | ||
| 420 | private static int getColumnWidthPx(Sheet sheet, int colIndex) { | 707 | private static int getColumnWidthPx(Sheet sheet, int colIndex) { |
| 421 | int widthUnits = sheet.getColumnWidth(colIndex); | 708 | int widthUnits = sheet.getColumnWidth(colIndex); |
| 422 | return Math.max(1, (int) Math.round(widthUnits / 256.0 * 7.0 + 5)); | 709 | return Math.max(1, (int) Math.round(widthUnits / 256.0 * 7.0 + 5)); |
| @@ -428,6 +715,155 @@ public class LatexFormulaExcelExporterUtil { | @@ -428,6 +715,155 @@ public class LatexFormulaExcelExporterUtil { | ||
| 428 | return Math.max(1, (int) Math.round(points * 96.0 / 72.0)); | 715 | return Math.max(1, (int) Math.round(points * 96.0 / 72.0)); |
| 429 | } | 716 | } |
| 430 | 717 | ||
| 718 | + private static int renderBestFitLatexFontSize(String latex, int preferredFontSize, | ||
| 719 | + int targetWidthPx, int targetHeightPx) throws IOException { | ||
| 720 | + int minFontSize = 8; | ||
| 721 | + int maxFontSize = Math.max(minFontSize, preferredFontSize + 40); | ||
| 722 | + maxFontSize = Math.min(96, maxFontSize); | ||
| 723 | + int left = minFontSize; | ||
| 724 | + int right = maxFontSize; | ||
| 725 | + int bestFontSize = minFontSize; | ||
| 726 | + | ||
| 727 | + while (left <= right) { | ||
| 728 | + int mid = (left + right) >>> 1; | ||
| 729 | + RenderedLatexImage candidate = renderLatexImage(latex, mid); | ||
| 730 | + if (fitsWithin(candidate, targetWidthPx, targetHeightPx)) { | ||
| 731 | + bestFontSize = mid; | ||
| 732 | + left = mid + 1; | ||
| 733 | + } else { | ||
| 734 | + right = mid - 1; | ||
| 735 | + } | ||
| 736 | + } | ||
| 737 | + return bestFontSize; | ||
| 738 | + } | ||
| 739 | + | ||
| 740 | + private static RenderedLatexImage renderLatexImage(String latex, int fontSize) throws IOException { | ||
| 741 | + byte[] imageBytes = latexToContractImageBytes(latex, fontSize); | ||
| 742 | + BufferedImage image; | ||
| 743 | + try (ByteArrayInputStream in = new ByteArrayInputStream(imageBytes)) { | ||
| 744 | + image = ImageIO.read(in); | ||
| 745 | + } | ||
| 746 | + return new RenderedLatexImage(imageBytes, image); | ||
| 747 | + } | ||
| 748 | + | ||
| 749 | + private static byte[] latexToContractImageBytes(String latex, int fontSize) throws IOException { | ||
| 750 | + try { | ||
| 751 | + double scale = 5.0; | ||
| 752 | + TeXFormula formula = new TeXFormula(latex); | ||
| 753 | + TeXIcon icon = formula.createTeXIcon(TeXConstants.STYLE_DISPLAY, fontSize); | ||
| 754 | + | ||
| 755 | + int baseWidth = icon.getIconWidth(); | ||
| 756 | + int baseHeight = icon.getIconHeight(); | ||
| 757 | + if (baseWidth <= 0 || baseHeight <= 0) { | ||
| 758 | + return new byte[0]; | ||
| 759 | + } | ||
| 760 | + | ||
| 761 | + int maxWidth = 4200; | ||
| 762 | + int maxHeight = 3200; | ||
| 763 | + double widthScale = (double) maxWidth / baseWidth; | ||
| 764 | + double heightScale = (double) maxHeight / baseHeight; | ||
| 765 | + scale = Math.min(scale, Math.min(widthScale, heightScale)); | ||
| 766 | + scale = Math.max(1.0, scale); | ||
| 767 | + | ||
| 768 | + int scaledWidth = Math.max(1, (int) Math.ceil(baseWidth * scale)); | ||
| 769 | + int scaledHeight = Math.max(1, (int) Math.ceil(baseHeight * scale)); | ||
| 770 | + BufferedImage image = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_RGB); | ||
| 771 | + Graphics2D g2 = image.createGraphics(); | ||
| 772 | + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | ||
| 773 | + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); | ||
| 774 | + g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); | ||
| 775 | + g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); | ||
| 776 | + g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); | ||
| 777 | + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); | ||
| 778 | + | ||
| 779 | + java.awt.Font renderFont = new java.awt.Font("宋体", java.awt.Font.BOLD, fontSize); | ||
| 780 | + g2.setFont(renderFont); | ||
| 781 | + g2.setColor(Color.WHITE); | ||
| 782 | + g2.fillRect(0, 0, scaledWidth, scaledHeight); | ||
| 783 | + g2.scale(scale, scale); | ||
| 784 | + | ||
| 785 | + JLabel label = new JLabel(); | ||
| 786 | + label.setFont(renderFont); | ||
| 787 | + label.setForeground(Color.BLACK); | ||
| 788 | + icon.paintIcon(label, g2, 0, 0); | ||
| 789 | + g2.dispose(); | ||
| 790 | + | ||
| 791 | + image = trimWhitespace(image, 248, 0); | ||
| 792 | + ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
| 793 | + ImageIO.write(image, "png", baos); | ||
| 794 | + return baos.toByteArray(); | ||
| 795 | + } catch (Exception e) { | ||
| 796 | + throw new IOException("合同规格图片渲染失败: " + e.getMessage(), e); | ||
| 797 | + } | ||
| 798 | + } | ||
| 799 | + | ||
| 800 | + private static boolean fitsWithin(RenderedLatexImage renderedImage, int targetWidthPx, int targetHeightPx) { | ||
| 801 | + return renderedImage != null | ||
| 802 | + && renderedImage.image != null | ||
| 803 | + && renderedImage.image.getWidth() <= targetWidthPx | ||
| 804 | + && renderedImage.image.getHeight() <= targetHeightPx; | ||
| 805 | + } | ||
| 806 | + | ||
| 807 | + private static BufferedImage trimWhitespace(BufferedImage source, int whiteThreshold, int marginPx) { | ||
| 808 | + if (source == null || source.getWidth() <= 0 || source.getHeight() <= 0) { | ||
| 809 | + return source; | ||
| 810 | + } | ||
| 811 | + | ||
| 812 | + int minX = source.getWidth(); | ||
| 813 | + int minY = source.getHeight(); | ||
| 814 | + int maxX = -1; | ||
| 815 | + int maxY = -1; | ||
| 816 | + | ||
| 817 | + for (int y = 0; y < source.getHeight(); y++) { | ||
| 818 | + for (int x = 0; x < source.getWidth(); x++) { | ||
| 819 | + if (!isNearWhite(source.getRGB(x, y), whiteThreshold)) { | ||
| 820 | + minX = Math.min(minX, x); | ||
| 821 | + minY = Math.min(minY, y); | ||
| 822 | + maxX = Math.max(maxX, x); | ||
| 823 | + maxY = Math.max(maxY, y); | ||
| 824 | + } | ||
| 825 | + } | ||
| 826 | + } | ||
| 827 | + | ||
| 828 | + if (maxX < minX || maxY < minY) { | ||
| 829 | + return source; | ||
| 830 | + } | ||
| 831 | + | ||
| 832 | + int cropMinX = Math.max(0, minX - marginPx); | ||
| 833 | + int cropMinY = Math.max(0, minY - marginPx); | ||
| 834 | + int cropMaxX = Math.min(source.getWidth() - 1, maxX + marginPx); | ||
| 835 | + int cropMaxY = Math.min(source.getHeight() - 1, maxY + marginPx); | ||
| 836 | + int cropWidth = cropMaxX - cropMinX + 1; | ||
| 837 | + int cropHeight = cropMaxY - cropMinY + 1; | ||
| 838 | + | ||
| 839 | + if (cropWidth >= source.getWidth() && cropHeight >= source.getHeight()) { | ||
| 840 | + return source; | ||
| 841 | + } | ||
| 842 | + | ||
| 843 | + BufferedImage trimmed = new BufferedImage(cropWidth, cropHeight, BufferedImage.TYPE_INT_RGB); | ||
| 844 | + Graphics2D g2 = trimmed.createGraphics(); | ||
| 845 | + g2.drawImage(source.getSubimage(cropMinX, cropMinY, cropWidth, cropHeight), 0, 0, null); | ||
| 846 | + g2.dispose(); | ||
| 847 | + return trimmed; | ||
| 848 | + } | ||
| 849 | + | ||
| 850 | + private static boolean isNearWhite(int rgb, int whiteThreshold) { | ||
| 851 | + int red = (rgb >> 16) & 0xFF; | ||
| 852 | + int green = (rgb >> 8) & 0xFF; | ||
| 853 | + int blue = rgb & 0xFF; | ||
| 854 | + return red >= whiteThreshold && green >= whiteThreshold && blue >= whiteThreshold; | ||
| 855 | + } | ||
| 856 | + | ||
| 857 | + private static class RenderedLatexImage { | ||
| 858 | + final byte[] imageBytes; | ||
| 859 | + final BufferedImage image; | ||
| 860 | + | ||
| 861 | + private RenderedLatexImage(byte[] imageBytes, BufferedImage image) { | ||
| 862 | + this.imageBytes = imageBytes; | ||
| 863 | + this.image = image; | ||
| 864 | + } | ||
| 865 | + } | ||
| 866 | + | ||
| 431 | private static class AnchorCoord { | 867 | private static class AnchorCoord { |
| 432 | final int index; | 868 | final int index; |
| 433 | final int offsetPx; | 869 | final int offsetPx; |
No preview for this file type
No preview for this file type
No preview for this file type