diff --git a/README.md b/README.md index 0bf6de33..198beb18 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ features, known gaps, and development workflow. io.github.mini-software minipdf - 0.1.2 + 0.1.3 ``` diff --git a/artifacts/java-benchmark/issue/xlsx/report/benchmark_coverage.json b/artifacts/java-benchmark/issue/xlsx/report/benchmark_coverage.json index df7a6d82..d53cc25c 100644 --- a/artifacts/java-benchmark/issue/xlsx/report/benchmark_coverage.json +++ b/artifacts/java-benchmark/issue/xlsx/report/benchmark_coverage.json @@ -16,14 +16,14 @@ "missing_auxiliary_references": 0, "comparison_completed": true, "comparison_results": 1, - "average_score": 0.9001, + "average_score": 0.9672, "cases": [ { - "name": "Issue202609031340", - "case_id": "Issue202609031340", + "name": "XlsxIssue77_Template1", + "case_id": "XlsxIssue77_Template1", "suite": "issue", "format": "xlsx", - "source_path": "tests/Issue_Files/xlsx/Issue202609031340.xlsx", + "source_path": "tests/Issue_Files/xlsx/XlsxIssue77_Template1.xlsx", "conversion_status": "passed", "conversion_exit_code": 0, "candidate_exists": true, diff --git a/artifacts/java-benchmark/issue/xlsx/report/comparison_manifest.json b/artifacts/java-benchmark/issue/xlsx/report/comparison_manifest.json index e8821b1c..d2335c9b 100644 --- a/artifacts/java-benchmark/issue/xlsx/report/comparison_manifest.json +++ b/artifacts/java-benchmark/issue/xlsx/report/comparison_manifest.json @@ -1,11 +1,11 @@ { "cases": [ { - "name": "Issue202609031340", - "case_id": "Issue202609031340", + "name": "XlsxIssue77_Template1", + "case_id": "XlsxIssue77_Template1", "suite": "issue", "format": "xlsx", - "source_path": "tests/Issue_Files/xlsx/Issue202609031340.xlsx", + "source_path": "tests/Issue_Files/xlsx/XlsxIssue77_Template1.xlsx", "conversion_status": "pending", "conversion_exit_code": null, "candidate_exists": false, diff --git a/artifacts/java-benchmark/issue/xlsx/report/comparison_report.json b/artifacts/java-benchmark/issue/xlsx/report/comparison_report.json index 5838e1b5..1b64c6f3 100644 --- a/artifacts/java-benchmark/issue/xlsx/report/comparison_report.json +++ b/artifacts/java-benchmark/issue/xlsx/report/comparison_report.json @@ -1,135 +1,189 @@ [ { - "name": "Issue202609031340", + "name": "XlsxIssue77_Template1", "minipdf_exists": true, "reference_exists": true, "report_scope": "java-issue-xlsx", - "case_id": "Issue202609031340", + "case_id": "XlsxIssue77_Template1", "format": "xlsx", - "source_path": "tests/Issue_Files/xlsx/Issue202609031340.xlsx", + "source_path": "tests/Issue_Files/xlsx/XlsxIssue77_Template1.xlsx", "suite": "issue", - "minipdf_size": 446239, - "reference_size": 346206, + "minipdf_size": 59636, + "reference_size": 64464, "pdf_valid": true, - "minipdf_pages": 4, - "reference_pages": 4, - "text_similarity": 0.8982, - "flat_text_similarity": 0.8982, - "word_text_similarity": 0.8147, - "text_diff": "--- minipdf/Issue202609031340.pdf\n+++ reference/Issue202609031340.pdf\n@@ -1,10 +1,10 @@\n 表单编号 XXX-XXX-051-01\n\n-文件编号QA-SIPI-XXXX-01-1-1\n\n-XXXXXX有限公司\n\n+XXXXXX有限公司 文件编号QA-SIPI-XXXX-01-1-1\n\n 发行日期 2025-04-11\n\n 文件版本 03 修订日期 2025-11-25\n\n 产品穴号 详见注意事项 产品单重 20.45±1.05 g\n\n-注塑制程检验标准书 ¢1.18*10MM(320度加强\n\n+注塑制程检验标准书\n\n+¢1.18*10MM(320度加强\n\n 附件编号 / 销钉规格\n\n 型)\n\n 产品型号 XXXX 部件名称 盖塑胶件 抽样计划 ANSI/ASQ Z1.4Ⅱ 巡检频率 2H/次\n\n@@ -15,16 +15,20 @@\n 参考 图纸文件编号:XXXX系列 内部图纸 2025.4.10\n\n 项目 检验工具 巡检内容\n\n 图 部件图纸编号:HKXXXX-01-RB\n\n-结 目视、图纸 1、首件样品整模结构符合图纸设计、同PPS结构一致;\n\n-构 、PPS 2、制程巡检整模产品结构需同首件结构一致。\n\n-颜 目视、限度 1、首件样品颜色需无限接近限度样中的标准样;\n\n-色 样 2、制程巡检产品颜色需同首件颜色一致。\n\n-外 目视、限度 1、注塑成型外观缺陷大小不可超出限度样板;\n\n-观 样 2、其它外观缺陷无限度样的按常规通用标准判定。\n\n+结 目视、图 1、首件样品整模结构符合图纸设计、同PPS结构一致;\n\n+构 纸、PPS 2、制程巡检整模产品结构需同首件结构一致。\n\n+颜 目视、限 1、首件样品颜色需无限接近限度样中的标准样;\n\n+色 度样 2、制程巡检产品颜色需同首件颜色一致。\n\n+外 目视、限 1、注塑成型外观缺陷大小不可超出限度样板;\n\n+观 度样 2、其它外观缺陷无限度样的按常规通用标准判定。\n\n 包装 目视 参照注塑作业指导书(SOP)\n\n 仪器 项目 管控标准(mm) 测量位置 频率\n\n-卡尺 A 68.16 +0.05/-0.17 外形最长距离 1次/\n\n-卡尺 B 62.03 +0.05/-0.20 外形最宽距离 1次/\n\n+1次/\n\n+卡尺 A 68.16 +0.05/-0.17 外形最长距离\n\n+班\n\n+1次/\n\n+卡尺 B 62.03 +0.05/-0.20 外形最宽距离\n\n+班\n\n 卡尺 C 27.9 +0.03/-0.05 顶部装饰片槽长度 首件\n\n 卡尺 D 18.87 +0.00/-0.10 后钮侧面装饰片槽长度 首件\n\n 卡尺 E 6.70+0.00/-0.05 后钮侧面大卡槽宽度 首件\n\n@@ -36,52 +40,74 @@\n 后钮针孔中分线中心点至前扣弧\n\n 投影仪 J 53.93+0.04/-0.03 6H\n\n 线中心点距离\n\n-卡尺 K 18.61±0.05 后钮针孔左右间距 1次/\n\n-高度规 L*2 4.65+0.00/-0.10 后钮针孔中心至口部平面间距 1次/\n\n-参考资料/项目 代码 取样数量频率 代码 取样数量频率 注意事项及品质履历\n\n+1次/\n\n+卡尺 K 18.61±0.05 后钮针孔左右间距\n\n+班\n\n+1次/\n\n+高度规 L*2 4.65+0.00/-0.10 后钮针孔中心至口部平面间距\n\n+班\n\n+产品后钮装饰片槽缺\n\n+口竖边同卡尺贴紧\n\n+68.16 mm 62.03 mm\n\n+产品口部平面同\n\n+产品口部平面同\n\n+卡尺上平面平行\n\n+卡尺上平面平行\n\n+产品后钮装饰片槽缺\n\n+口竖边同卡尺贴紧\n\n+0.00 ~ 300mm卡尺\n\n+参考资料/项目 代码 取样数量 频率 代码 取样数量 频率 注意事项及品质履历\n\n 1.MO25XXXX0100:1#~4#/MO25XXXX0110:5#~12#\n\n 尺\n\n 寸\n\n 组\n\n 组装制程检验标\n\n-准书 1# 一模 1次/ 2# 一模 1次/\n\n+1次/ 1次/\n\n+准书 1# 一模 2# 一模\n\n+班 班\n\n 合\n\n-XXXX-00组装成\n\n+XXXX-\n\n 功\n\n-3# 一模 1次/ 5# 一模 1次/\n\n-品\n\n+1次/ 1次/\n\n+3# 一模 5# 一模\n\n+00组装成品 班 班\n\n 能\n\n-6# 一模 1次/\n\n-日期 版本工程担当 品质担当 修 订 内 容\n\n+1次/\n\n+6# 一模\n\n+班\n\n+日期 版本 工程担当 品质担当 修 订 内 容\n\n+2025/4/11 00 AAAA AAA 初版发行\n\n 修\n\n+---PAGE---\n\n+修\n\n+A:68.16 +0.05/-0.13改为68.16 +0.05/-0.17;B:62.03 +0.05/-0.10改为62.03 +0.05/-0.20;D:18.87\n\n 订\n\n-2025/4/11 00 AAAA AAA 初版发行\n\n----PAGE---\n\n-A:68.16 +0.05/-0.13改为68.16 +0.05/-0.17;B:62.03 +0.05/-0.10改为62.03 +0.05/-0.20;D:18.87\n\n-2025/5/12 01 BBBB BBB +0.00/-0.05改为18.87 +0.00/-0.10;I:8.35+0.00/-0.16改为8.35+0.10/-0.05;J:53.93+0.00/-0.10改\n\n-为53.93+0.05/-0.10;L*2:4.65±0.05改成4.65+0.00/-0.10;更新检验频率\n\n++0.00/-0.05改为18.87\n\n+2025/5/12 01 BBBB BBB\n\n+记\n\n++0.00/-0.10;I:8.35+0.00/-0.16改为8.35+0.10/-0.05;J:53.93+0.00/-0.10改为53.93+0.05/-0.10;\n\n+录 L*2:4.65±0.05改成4.65+0.00/-0.10;更新检验频率\n\n 2025/8/6 02 CCCC CCCC 新增模具编号:MO25XXXX0110\n\n 2025/11/25 03 DDDD DDDD J:53.93+0.05/-0.10改为53.93+0.04/-0.03\n\n-研 制 制 品\n\n-发 造 造 保\n\n-制\n\n-工 工 部 部\n\n-作\n\n-程 程 审 审\n\n-审 审 核 核\n\n+研 制\n\n+制 品\n\n+审 发 审 造 审 审 制\n\n+造 保\n\n+核 工 核 工 核 核 作\n\n+部 部\n\n+程 程\n\n ---PAGE---\n\n-QA-SIPI-XXXX-01-1\n\n-文件编号 表单编号 QA-FM-051-01\n\n--1\n\n+文件编号 QA-SIPI-XXXX-01-1-1 表单编号 QA-FM-051-01\n\n XXXXXX有限公司\n\n 产品型号 XXXX 部件名称 /\n\n XXXXXX Co.,Ltd.\n\n 模具编号 / 产品穴号 /\n\n 部件材质 / 附件编号 /\n\n 产品品质履历(SIP附件)\n\n-版 日期 问题描述 图片\n\n-00 7/30/25 口部有麻点印NG,图1\n\n+版本 日期 问题描述 图片\n\n+00 7/30/2025 口部有麻点印NG,图1\n\n+图 1\n\n+---PAGE---\n\n 审 制\n\n 刘伟鹏\n\n-核 作\n\n----PAGE---\n+核 作", + "minipdf_pages": 6, + "reference_pages": 6, + "text_similarity": 1.0, + "flat_text_similarity": 0.9957, + "word_text_similarity": 1.0, + "text_diff": "--- minipdf/XlsxIssue77_Template1.pdf\n+++ reference/XlsxIssue77_Template1.pdf\n@@ -6,8 +6,8 @@\n Attn Customer: Please return parts to the return address below.\n\n Customer Name Sales Person Requestor\n\n Return Address: Reason for Return:\n\n-NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable\n\n-QR code, original factory packaging, with no extra labels or markings on the product.\n\n+NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable QR\n\n+code, original factory packaging, with no extra labels or markings on the product.\n\n Original Qty\n\n Part No. Per Unit\n\n Invoice # Authorized\n\n@@ -20,8 +20,8 @@\n Attn Customer: Please return parts to the return address below.\n\n Customer Name Sales Person Requestor\n\n Return Address: Reason for Return:\n\n-NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable\n\n-QR code, original factory packaging, with no extra labels or markings on the product.\n\n+NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable QR\n\n+code, original factory packaging, with no extra labels or markings on the product.\n\n Original Qty\n\n Part No. Per Unit\n\n Invoice # Authorized\n\n@@ -34,8 +34,8 @@\n Attn Customer: Please return parts to the return address below.\n\n Customer Name Sales Person Requestor\n\n Return Address: Reason for Return:\n\n-NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable\n\n-QR code, original factory packaging, with no extra labels or markings on the product.\n\n+NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable QR\n\n+code, original factory packaging, with no extra labels or markings on the product.\n\n Original Qty\n\n Part No. Per Unit\n\n Invoice # Authorized\n\n@@ -48,8 +48,8 @@\n Attn Customer: Please return parts to the return address below.\n\n Customer Name Sales Person Requestor\n\n Return Address: Reason for Return:\n\n-NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable\n\n-QR code, original factory packaging, with no extra labels or markings on the product.\n\n+NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable QR\n\n+code, original factory packaging, with no extra labels or markings on the product.\n\n Original Qty\n\n Part No. Per Unit\n\n Invoice # Authorized\n\n@@ -62,8 +62,8 @@\n Attn Customer: Please return parts to the return address below.\n\n Customer Name Sales Person Requestor\n\n Return Address: Reason for Return:\n\n-NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable\n\n-QR code, original factory packaging, with no extra labels or markings on the product.\n\n+NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable QR\n\n+code, original factory packaging, with no extra labels or markings on the product.\n\n Original Qty\n\n Part No. Per Unit\n\n Invoice # Authorized\n\n@@ -76,8 +76,8 @@\n Attn Customer: Please return parts to the return address below.\n\n Customer Name Sales Person Requestor\n\n Return Address: Reason for Return:\n\n-NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable\n\n-QR code, original factory packaging, with no extra labels or markings on the product.\n\n+NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable QR\n\n+code, original factory packaging, with no extra labels or markings on the product.\n\n Original Qty\n\n Part No. Per Unit\n\n Invoice # Authorized", "visual_scores": [ - 0.9062, - 0.9863, - 0.8805, - 0.6351 + 0.9133, + 0.9135, + 0.9135, + 0.9135, + 0.9135, + 0.9406 ], - "visual_avg": 0.852, + "visual_avg": 0.918, "diff_images": [ { "page": 1, - "minipdf_img": "Issue202609031340_p1_minipdf.png", - "reference_img": "Issue202609031340_p1_reference.png", - "auxiliary_img": "Issue202609031340_p1_libreoffice.png", - "heatmap_img": "Issue202609031340_p1_heatmap.png", + "minipdf_img": "XlsxIssue77_Template1_p1_minipdf.png", + "reference_img": "XlsxIssue77_Template1_p1_reference.png", + "auxiliary_img": "XlsxIssue77_Template1_p1_libreoffice.png", + "heatmap_img": "XlsxIssue77_Template1_p1_heatmap.png", "heatmap_metrics": { "size_px": [ 1241, 1754 ], "difference_bbox_px": [ - 43, - 55, - 1197, - 1697 + 82, + 168, + 1157, + 1581 ], - "changed_pixels": 402314, - "changed_fraction": 0.184826, - "mean_abs_rgb": 23.5695, - "rmse_rgb": 65.468, + "changed_pixels": 334581, + "changed_fraction": 0.153709, + "mean_abs_rgb": 20.9601, + "rmse_rgb": 63.4759, "threshold": 12, "gain": 5.0 }, - "composite_img": "Issue202609031340_p1_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png" + "composite_img": "XlsxIssue77_Template1_p1_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png" }, { "page": 2, - "minipdf_img": "Issue202609031340_p2_minipdf.png", - "reference_img": "Issue202609031340_p2_reference.png", - "auxiliary_img": "Issue202609031340_p2_libreoffice.png", - "heatmap_img": "Issue202609031340_p2_heatmap.png", + "minipdf_img": "XlsxIssue77_Template1_p2_minipdf.png", + "reference_img": "XlsxIssue77_Template1_p2_reference.png", + "auxiliary_img": "XlsxIssue77_Template1_p2_libreoffice.png", + "heatmap_img": "XlsxIssue77_Template1_p2_heatmap.png", "heatmap_metrics": { "size_px": [ 1241, 1754 ], "difference_bbox_px": [ - 43, - 738, - 1197, - 1014 + 82, + 168, + 1157, + 1581 ], - "changed_pixels": 55108, - "changed_fraction": 0.025317, - "mean_abs_rgb": 3.3916, - "rmse_rgb": 24.2256, + "changed_pixels": 333843, + "changed_fraction": 0.15337, + "mean_abs_rgb": 20.8471, + "rmse_rgb": 63.2311, "threshold": 12, "gain": 5.0 }, - "composite_img": "Issue202609031340_p2_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png" + "composite_img": "XlsxIssue77_Template1_p2_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png" }, { "page": 3, - "minipdf_img": "Issue202609031340_p3_minipdf.png", - "reference_img": "Issue202609031340_p3_reference.png", - "auxiliary_img": "Issue202609031340_p3_libreoffice.png", - "heatmap_img": "Issue202609031340_p3_heatmap.png", + "minipdf_img": "XlsxIssue77_Template1_p3_minipdf.png", + "reference_img": "XlsxIssue77_Template1_p3_reference.png", + "auxiliary_img": "XlsxIssue77_Template1_p3_libreoffice.png", + "heatmap_img": "XlsxIssue77_Template1_p3_heatmap.png", "heatmap_metrics": { "size_px": [ 1241, 1754 ], "difference_bbox_px": [ - 34, - 29, - 1196, - 1725 + 82, + 168, + 1157, + 1581 ], - "changed_pixels": 262475, - "changed_fraction": 0.120583, - "mean_abs_rgb": 17.2968, - "rmse_rgb": 56.7183, + "changed_pixels": 333852, + "changed_fraction": 0.153374, + "mean_abs_rgb": 20.8014, + "rmse_rgb": 63.1465, "threshold": 12, "gain": 5.0 }, - "composite_img": "Issue202609031340_p3_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png" + "composite_img": "XlsxIssue77_Template1_p3_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png" }, { "page": 4, - "minipdf_img": "Issue202609031340_p4_minipdf.png", - "reference_img": "Issue202609031340_p4_reference.png", - "auxiliary_img": "Issue202609031340_p4_libreoffice.png", - "heatmap_img": "Issue202609031340_p4_heatmap.png", + "minipdf_img": "XlsxIssue77_Template1_p4_minipdf.png", + "reference_img": "XlsxIssue77_Template1_p4_reference.png", + "auxiliary_img": "XlsxIssue77_Template1_p4_libreoffice.png", + "heatmap_img": "XlsxIssue77_Template1_p4_heatmap.png", "heatmap_metrics": { "size_px": [ 1241, 1754 ], "difference_bbox_px": [ - 42, - 35, - 1196, - 1009 + 82, + 168, + 1157, + 1581 ], - "changed_pixels": 16586, - "changed_fraction": 0.00762, - "mean_abs_rgb": 1.3227, - "rmse_rgb": 16.6519, + "changed_pixels": 333854, + "changed_fraction": 0.153375, + "mean_abs_rgb": 20.8086, + "rmse_rgb": 63.1615, "threshold": 12, "gain": 5.0 }, - "composite_img": "Issue202609031340_p4_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png" + "composite_img": "XlsxIssue77_Template1_p4_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png" + }, + { + "page": 5, + "minipdf_img": "XlsxIssue77_Template1_p5_minipdf.png", + "reference_img": "XlsxIssue77_Template1_p5_reference.png", + "auxiliary_img": "XlsxIssue77_Template1_p5_libreoffice.png", + "heatmap_img": "XlsxIssue77_Template1_p5_heatmap.png", + "heatmap_metrics": { + "size_px": [ + 1241, + 1754 + ], + "difference_bbox_px": [ + 82, + 168, + 1157, + 1581 + ], + "changed_pixels": 333852, + "changed_fraction": 0.153374, + "mean_abs_rgb": 20.8093, + "rmse_rgb": 63.1483, + "threshold": 12, + "gain": 5.0 + }, + "composite_img": "XlsxIssue77_Template1_p5_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png" + }, + { + "page": 6, + "minipdf_img": "XlsxIssue77_Template1_p6_minipdf.png", + "reference_img": "XlsxIssue77_Template1_p6_reference.png", + "auxiliary_img": "XlsxIssue77_Template1_p6_libreoffice.png", + "heatmap_img": "XlsxIssue77_Template1_p6_heatmap.png", + "heatmap_metrics": { + "size_px": [ + 1241, + 1754 + ], + "difference_bbox_px": [ + 82, + 168, + 1157, + 916 + ], + "changed_pixels": 200884, + "changed_fraction": 0.092288, + "mean_abs_rgb": 11.6525, + "rmse_rgb": 47.1047, + "threshold": 12, + "gain": 5.0 + }, + "composite_img": "XlsxIssue77_Template1_p6_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png" } ], - "overall_score": 0.9001 + "overall_score": 0.9672 } ] \ No newline at end of file diff --git a/artifacts/java-benchmark/issue/xlsx/report/comparison_report.md b/artifacts/java-benchmark/issue/xlsx/report/comparison_report.md index ef381de6..2a94e7f8 100644 --- a/artifacts/java-benchmark/issue/xlsx/report/comparison_report.md +++ b/artifacts/java-benchmark/issue/xlsx/report/comparison_report.md @@ -1,34 +1,42 @@ # java MiniPdf vs Microsoft 365 Excel Reference PDF Comparison Report -Generated: 2026-09-05T22:52:43.615504 +Generated: 2026-09-06T15:38:50.611350 ## Summary | # | Test Case | Valid | Text Sim | Visual Avg | Pages (M/R) | Overall | |---|-----------|-------|----------|------------|-------------|--------| -| 1 | 🟢 Issue202609031340 | ✅ | 0.8982 | 0.852 | 4/4 | **0.9001** | +| 1 | 🟢 XlsxIssue77_Template1 | ✅ | 1.0 | 0.918 | 6/6 | **0.9672** | -**Average Overall Score: 0.9001** +**Average Overall Score: 0.9672** ## Labeled Side-by-Side Comparison - - + + - - + + - - + + - - + + + + + + + + + +
CaseComparison
Issue202609031340
format: xlsx | case: Issue202609031340 | scope: java-issue-xlsx

Page 1
Issue202609031340 page 1 comparisonXlsxIssue77_Template1
format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx

Page 1
XlsxIssue77_Template1 page 1 comparison
Issue202609031340
format: xlsx | case: Issue202609031340 | scope: java-issue-xlsx

Page 2
Issue202609031340 page 2 comparisonXlsxIssue77_Template1
format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx

Page 2
XlsxIssue77_Template1 page 2 comparison
Issue202609031340
format: xlsx | case: Issue202609031340 | scope: java-issue-xlsx

Page 3
Issue202609031340 page 3 comparisonXlsxIssue77_Template1
format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx

Page 3
XlsxIssue77_Template1 page 3 comparison
Issue202609031340
format: xlsx | case: Issue202609031340 | scope: java-issue-xlsx

Page 4
Issue202609031340 page 4 comparisonXlsxIssue77_Template1
format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx

Page 4
XlsxIssue77_Template1 page 4 comparison
XlsxIssue77_Template1
format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx

Page 5
XlsxIssue77_Template1 page 5 comparison
XlsxIssue77_Template1
format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx

Page 6
XlsxIssue77_Template1 page 6 comparison
@@ -39,24 +47,34 @@ Blue areas are below the configured difference threshold; red areas have stronge - - - + + + - - - + + + - - - + + + - - - + + + + + + + + + + + + +
CaseHeatmapMetrics
Issue202609031340
Page 1
Issue202609031340 page 1 difference heatmapchanged: 402314 px (18.48%)
bbox: [43, 55, 1197, 1697]
mean abs RGB: 23.5695
RMSE RGB: 65.468
threshold: 12, gain: 5.0
XlsxIssue77_Template1
Page 1
XlsxIssue77_Template1 page 1 difference heatmapchanged: 334581 px (15.37%)
bbox: [82, 168, 1157, 1581]
mean abs RGB: 20.9601
RMSE RGB: 63.4759
threshold: 12, gain: 5.0
Issue202609031340
Page 2
Issue202609031340 page 2 difference heatmapchanged: 55108 px (2.53%)
bbox: [43, 738, 1197, 1014]
mean abs RGB: 3.3916
RMSE RGB: 24.2256
threshold: 12, gain: 5.0
XlsxIssue77_Template1
Page 2
XlsxIssue77_Template1 page 2 difference heatmapchanged: 333843 px (15.34%)
bbox: [82, 168, 1157, 1581]
mean abs RGB: 20.8471
RMSE RGB: 63.2311
threshold: 12, gain: 5.0
Issue202609031340
Page 3
Issue202609031340 page 3 difference heatmapchanged: 262475 px (12.06%)
bbox: [34, 29, 1196, 1725]
mean abs RGB: 17.2968
RMSE RGB: 56.7183
threshold: 12, gain: 5.0
XlsxIssue77_Template1
Page 3
XlsxIssue77_Template1 page 3 difference heatmapchanged: 333852 px (15.34%)
bbox: [82, 168, 1157, 1581]
mean abs RGB: 20.8014
RMSE RGB: 63.1465
threshold: 12, gain: 5.0
Issue202609031340
Page 4
Issue202609031340 page 4 difference heatmapchanged: 16586 px (0.76%)
bbox: [42, 35, 1196, 1009]
mean abs RGB: 1.3227
RMSE RGB: 16.6519
threshold: 12, gain: 5.0
XlsxIssue77_Template1
Page 4
XlsxIssue77_Template1 page 4 difference heatmapchanged: 333854 px (15.34%)
bbox: [82, 168, 1157, 1581]
mean abs RGB: 20.8086
RMSE RGB: 63.1615
threshold: 12, gain: 5.0
XlsxIssue77_Template1
Page 5
XlsxIssue77_Template1 page 5 difference heatmapchanged: 333852 px (15.34%)
bbox: [82, 168, 1157, 1581]
mean abs RGB: 20.8093
RMSE RGB: 63.1483
threshold: 12, gain: 5.0
XlsxIssue77_Template1
Page 6
XlsxIssue77_Template1 page 6 difference heatmapchanged: 200884 px (9.23%)
bbox: [82, 168, 1157, 916]
mean abs RGB: 11.6525
RMSE RGB: 47.1047
threshold: 12, gain: 5.0
@@ -67,317 +85,155 @@ Scores compare java MiniPdf against Microsoft 365 Excel Reference. LibreOffice i - - + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + +
java MiniPdfMicrosoft 365 Excel ReferenceLibreOffice
Issue202609031340
format: xlsx | case: Issue202609031340 | scope: java-issue-xlsx
Issue202609031340 ⬤ 90.0%XlsxIssue77_Template1
format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx
XlsxIssue77_Template1 ⬤ 96.7%
java MiniPdfMicrosoft 365 Excel ReferenceLibreOffice
java MiniPdfMicrosoft 365 Excel ReferenceLibreOfficejava MiniPdfMicrosoft 365 Excel ReferenceLibreOffice
java MiniPdfMicrosoft 365 Excel ReferenceLibreOfficejava MiniPdfMicrosoft 365 Excel ReferenceLibreOffice
java MiniPdfMicrosoft 365 Excel ReferenceLibreOfficejava MiniPdfMicrosoft 365 Excel ReferenceLibreOffice
java MiniPdfMicrosoft 365 Excel ReferenceLibreOfficejava MiniPdfMicrosoft 365 Excel ReferenceLibreOffice
java MiniPdfMicrosoft 365 Excel ReferenceLibreOffice
## Detailed Results -### Issue202609031340 +### XlsxIssue77_Template1 -- **Case Metadata:** format: xlsx | case: Issue202609031340 | scope: java-issue-xlsx -- **Source:** tests/Issue_Files/xlsx/Issue202609031340.xlsx -- **Text Similarity:** 0.8982 -- **Visual Average:** 0.852 -- **Overall Score:** 0.9001 -- **Pages:** MiniPdf=4, Reference=4 -- **File Size:** MiniPdf=446239 bytes, Reference=346206 bytes +- **Case Metadata:** format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx +- **Source:** tests/Issue_Files/xlsx/XlsxIssue77_Template1.xlsx +- **Text Similarity:** 1.0 +- **Visual Average:** 0.918 +- **Overall Score:** 0.9672 +- **Pages:** MiniPdf=6, Reference=6 +- **File Size:** MiniPdf=59636 bytes, Reference=64464 bytes
Text Diff ```diff ---- minipdf/Issue202609031340.pdf -+++ reference/Issue202609031340.pdf -@@ -1,10 +1,10 @@ - 表单编号 XXX-XXX-051-01 - --文件编号QA-SIPI-XXXX-01-1-1 - --XXXXXX有限公司 - -+XXXXXX有限公司 文件编号QA-SIPI-XXXX-01-1-1 - - 发行日期 2025-04-11 - - 文件版本 03 修订日期 2025-11-25 - - 产品穴号 详见注意事项 产品单重 20.45±1.05 g - --注塑制程检验标准书 ¢1.18*10MM(320度加强 - -+注塑制程检验标准书 - -+¢1.18*10MM(320度加强 - - 附件编号 / 销钉规格 - - 型) - - 产品型号 XXXX 部件名称 盖塑胶件 抽样计划 ANSI/ASQ Z1.4Ⅱ 巡检频率 2H/次 - -@@ -15,16 +15,20 @@ - 参考 图纸文件编号:XXXX系列 内部图纸 2025.4.10 - - 项目 检验工具 巡检内容 - - 图 部件图纸编号:HKXXXX-01-RB - --结 目视、图纸 1、首件样品整模结构符合图纸设计、同PPS结构一致; - --构 、PPS 2、制程巡检整模产品结构需同首件结构一致。 - --颜 目视、限度 1、首件样品颜色需无限接近限度样中的标准样; - --色 样 2、制程巡检产品颜色需同首件颜色一致。 - --外 目视、限度 1、注塑成型外观缺陷大小不可超出限度样板; - --观 样 2、其它外观缺陷无限度样的按常规通用标准判定。 - -+结 目视、图 1、首件样品整模结构符合图纸设计、同PPS结构一致; - -+构 纸、PPS 2、制程巡检整模产品结构需同首件结构一致。 - -+颜 目视、限 1、首件样品颜色需无限接近限度样中的标准样; - -+色 度样 2、制程巡检产品颜色需同首件颜色一致。 - -+外 目视、限 1、注塑成型外观缺陷大小不可超出限度样板; - -+观 度样 2、其它外观缺陷无限度样的按常规通用标准判定。 - - 包装 目视 参照注塑作业指导书(SOP) - - 仪器 项目 管控标准(mm) 测量位置 频率 - --卡尺 A 68.16 +0.05/-0.17 外形最长距离 1次/ - --卡尺 B 62.03 +0.05/-0.20 外形最宽距离 1次/ - -+1次/ - -+卡尺 A 68.16 +0.05/-0.17 外形最长距离 - -+班 - -+1次/ - -+卡尺 B 62.03 +0.05/-0.20 外形最宽距离 - -+班 - - 卡尺 C 27.9 +0.03/-0.05 顶部装饰片槽长度 首件 - - 卡尺 D 18.87 +0.00/-0.10 后钮侧面装饰片槽长度 首件 - - 卡尺 E 6.70+0.00/-0.05 后钮侧面大卡槽宽度 首件 - -@@ -36,52 +40,74 @@ - 后钮针孔中分线中心点至前扣弧 - - 投影仪 J 53.93+0.04/-0.03 6H - - 线中心点距离 - --卡尺 K 18.61±0.05 后钮针孔左右间距 1次/ - --高度规 L*2 4.65+0.00/-0.10 后钮针孔中心至口部平面间距 1次/ - --参考资料/项目 代码 取样数量频率 代码 取样数量频率 注意事项及品质履历 - -+1次/ - -+卡尺 K 18.61±0.05 后钮针孔左右间距 - -+班 - -+1次/ - -+高度规 L*2 4.65+0.00/-0.10 后钮针孔中心至口部平面间距 - -+班 - -+产品后钮装饰片槽缺 - -+口竖边同卡尺贴紧 - -+68.16 mm 62.03 mm - -+产品口部平面同 - -+产品口部平面同 - -+卡尺上平面平行 - -+卡尺上平面平行 - -+产品后钮装饰片槽缺 - -+口竖边同卡尺贴紧 - -+0.00 ~ 300mm卡尺 - -+参考资料/项目 代码 取样数量 频率 代码 取样数量 频率 注意事项及品质履历 - - 1.MO25XXXX0100:1#~4#/MO25XXXX0110:5#~12# - - 尺 - - 寸 - - 组 - - 组装制程检验标 - --准书 1# 一模 1次/ 2# 一模 1次/ - -+1次/ 1次/ - -+准书 1# 一模 2# 一模 - -+班 班 - - 合 - --XXXX-00组装成 - -+XXXX- - - 功 - --3# 一模 1次/ 5# 一模 1次/ - --品 - -+1次/ 1次/ - -+3# 一模 5# 一模 - -+00组装成品 班 班 - - 能 - --6# 一模 1次/ - --日期 版本工程担当 品质担当 修 订 内 容 - -+1次/ - -+6# 一模 - -+班 +--- minipdf/XlsxIssue77_Template1.pdf ++++ reference/XlsxIssue77_Template1.pdf +@@ -6,8 +6,8 @@ + Attn Customer: Please return parts to the return address below. -+日期 版本 工程担当 品质担当 修 订 内 容 + Customer Name Sales Person Requestor -+2025/4/11 00 AAAA AAA 初版发行 + Return Address: Reason for Return: - 修 +-NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable -+---PAGE--- +-QR code, original factory packaging, with no extra labels or markings on the product. -+修 ++NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable QR -+A:68.16 +0.05/-0.13改为68.16 +0.05/-0.17;B:62.03 +0.05/-0.10改为62.03 +0.05/-0.20;D:18.87 ++code, original factory packaging, with no extra labels or markings on the product. - 订 + Original Qty --2025/4/11 00 AAAA AAA 初版发行 + Part No. Per Unit -----PAGE--- + Invoice # Authorized --A:68.16 +0.05/-0.13改为68.16 +0.05/-0.17;B:62.03 +0.05/-0.10改为62.03 +0.05/-0.20;D:18.87 +@@ -20,8 +20,8 @@ + Attn Customer: Please return parts to the return address below. --2025/5/12 01 BBBB BBB +0.00/-0.05改为18.87 +0.00/-0.10;I:8.35+0.00/-0.16改为8.35+0.10/-0.05;J:53.93+0.00/-0.10改 + Customer Name Sales Person Requestor --为53.93+0.05/-0.10;L*2:4.65±0.05改成4.65+0.00/-0.10;更新检验频率 + Return Address: Reason for Return: -++0.00/-0.05改为18.87 +-NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable -+2025/5/12 01 BBBB BBB +-QR code, original factory packaging, with no extra labels or markings on the product. -+记 ++NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable QR -++0.00/-0.10;I:8.35+0.00/-0.16改为8.35+0.10/-0.05;J:53.93+0.00/-0.10改为53.93+0.05/-0.10; ++code, original factory packaging, with no extra labels or markings on the product. -+录 L*2:4.65±0.05改成4.65+0.00/-0.10;更新检验频率 + Original Qty - 2025/8/6 02 CCCC CCCC 新增模具编号:MO25XXXX0110 + Part No. Per Unit - 2025/11/25 03 DDDD DDDD J:53.93+0.05/-0.10改为53.93+0.04/-0.03 + Invoice # Authorized --研 制 制 品 +@@ -34,8 +34,8 @@ + Attn Customer: Please return parts to the return address below. --发 造 造 保 + Customer Name Sales Person Requestor --制 + Return Address: Reason for Return: --工 工 部 部 +-NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable --作 +-QR code, original factory packaging, with no extra labels or markings on the product. --程 程 审 审 ++NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable QR --审 审 核 核 ++code, original factory packaging, with no extra labels or markings on the product. -+研 制 + Original Qty -+制 品 + Part No. Per Unit -+审 发 审 造 审 审 制 + Invoice # Authorized -+造 保 +@@ -48,8 +48,8 @@ + Attn Customer: Please return parts to the return address below. -+核 工 核 工 核 核 作 + Customer Name Sales Person Requestor -+部 部 + Return Address: Reason for Return: -+程 程 +-NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable - ---PAGE--- +-QR code, original factory packaging, with no extra labels or markings on the product. --QA-SIPI-XXXX-01-1 ++NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable QR --文件编号 表单编号 QA-FM-051-01 ++code, original factory packaging, with no extra labels or markings on the product. ---1 + Original Qty -+文件编号 QA-SIPI-XXXX-01-1-1 表单编号 QA-FM-051-01 + Part No. Per Unit - XXXXXX有限公司 + Invoice # Authorized - 产品型号 XXXX 部件名称 / +@@ -62,8 +62,8 @@ + Attn Customer: Please return parts to the return address below. - XXXXXX Co.,Ltd. + Customer Name Sales Person Requestor - 模具编号 / 产品穴号 / + Return Address: Reason for Return: - 部件材质 / 附件编号 / +-NOTE - All parts returned for credit must be in original factory condition, with clean factory label and scannable - 产品品质履历(SIP附件) +-QR code, original factory packaging, with no extra labels or markings on the product. --版 日期 问题 -... (128 more characters) ++NOTE - All parts returned for credit +... (854 more characters) ```
diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p1_heatmap.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p1_heatmap.png new file mode 100644 index 00000000..dc81532d Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p1_heatmap.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p1_libreoffice.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p1_libreoffice.png new file mode 100644 index 00000000..f8475a94 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p1_libreoffice.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p1_minipdf.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p1_minipdf.png new file mode 100644 index 00000000..2d14691a Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p1_minipdf.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p1_reference.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p1_reference.png new file mode 100644 index 00000000..5773c2dc Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p1_reference.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p2_heatmap.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p2_heatmap.png new file mode 100644 index 00000000..8fbc5e55 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p2_heatmap.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p2_libreoffice.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p2_libreoffice.png new file mode 100644 index 00000000..053de176 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p2_libreoffice.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p2_minipdf.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p2_minipdf.png new file mode 100644 index 00000000..7a9b1486 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p2_minipdf.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p2_reference.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p2_reference.png new file mode 100644 index 00000000..5060df90 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p2_reference.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p3_heatmap.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p3_heatmap.png new file mode 100644 index 00000000..0838f913 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p3_heatmap.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p3_libreoffice.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p3_libreoffice.png new file mode 100644 index 00000000..053de176 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p3_libreoffice.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p3_minipdf.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p3_minipdf.png new file mode 100644 index 00000000..92865149 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p3_minipdf.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p3_reference.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p3_reference.png new file mode 100644 index 00000000..5060df90 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p3_reference.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p4_heatmap.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p4_heatmap.png new file mode 100644 index 00000000..da9ee6f5 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p4_heatmap.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p4_libreoffice.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p4_libreoffice.png new file mode 100644 index 00000000..053de176 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p4_libreoffice.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p4_minipdf.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p4_minipdf.png new file mode 100644 index 00000000..d5349ca8 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p4_minipdf.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p4_reference.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p4_reference.png new file mode 100644 index 00000000..5060df90 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p4_reference.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p5_heatmap.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p5_heatmap.png new file mode 100644 index 00000000..0aff8bb5 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p5_heatmap.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p5_libreoffice.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p5_libreoffice.png new file mode 100644 index 00000000..053de176 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p5_libreoffice.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p5_minipdf.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p5_minipdf.png new file mode 100644 index 00000000..ce8d67e2 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p5_minipdf.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p5_reference.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p5_reference.png new file mode 100644 index 00000000..5060df90 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p5_reference.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p6_heatmap.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p6_heatmap.png new file mode 100644 index 00000000..20375893 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p6_heatmap.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p6_libreoffice.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p6_libreoffice.png new file mode 100644 index 00000000..f3783c5c Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p6_libreoffice.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p6_minipdf.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p6_minipdf.png new file mode 100644 index 00000000..d1a29fda Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p6_minipdf.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p6_reference.png b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p6_reference.png new file mode 100644 index 00000000..4a68440b Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/images/XlsxIssue77_Template1_p6_reference.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p1_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png b/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p1_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png new file mode 100644 index 00000000..7eb89464 Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p1_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p2_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png b/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p2_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png new file mode 100644 index 00000000..1e9d0b6b Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p2_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p3_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png b/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p3_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png new file mode 100644 index 00000000..52103f1c Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p3_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p4_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png b/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p4_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png new file mode 100644 index 00000000..732e37ed Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p4_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p5_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png b/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p5_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png new file mode 100644 index 00000000..fe8d328c Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p5_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png differ diff --git a/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p6_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png b/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p6_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png new file mode 100644 index 00000000..66c2e3ac Binary files /dev/null and b/artifacts/java-benchmark/issue/xlsx/report/side-by-side/XlsxIssue77_Template1_p6_java_minipdf_vs_microsoft_365_excel_reference_vs_libreoffice.png differ diff --git a/documents/README.fr.md b/documents/README.fr.md index ab864280..7fc4bf5e 100644 --- a/documents/README.fr.md +++ b/documents/README.fr.md @@ -85,7 +85,7 @@ Le [guide Rust](../minipdf-rs/README.md) décrit l'API du crate, la CLI, les fon io.github.mini-software minipdf - 0.1.2 + 0.1.3 ``` diff --git a/documents/README.it.md b/documents/README.it.md index 9311e8e8..685aed0a 100644 --- a/documents/README.it.md +++ b/documents/README.it.md @@ -85,7 +85,7 @@ La [guida Rust](../minipdf-rs/README.md) descrive API del crate, CLI, funzionali io.github.mini-software minipdf - 0.1.2 + 0.1.3 ``` diff --git a/documents/README.ja.md b/documents/README.ja.md index 8e2b0bde..0dda6c88 100644 --- a/documents/README.ja.md +++ b/documents/README.ja.md @@ -85,7 +85,7 @@ minipdf report.docx -o report.pdf io.github.mini-software minipdf - 0.1.2 + 0.1.3 ``` diff --git a/documents/README.ko.md b/documents/README.ko.md index dbac64db..77192d0e 100644 --- a/documents/README.ko.md +++ b/documents/README.ko.md @@ -85,7 +85,7 @@ minipdf report.docx -o report.pdf io.github.mini-software minipdf - 0.1.2 + 0.1.3 ``` diff --git a/documents/README.zh-CN.md b/documents/README.zh-CN.md index c84c2987..6b905353 100644 --- a/documents/README.zh-CN.md +++ b/documents/README.zh-CN.md @@ -85,7 +85,7 @@ minipdf report.docx -o report.pdf io.github.mini-software minipdf - 0.1.2 + 0.1.3 ``` diff --git a/documents/README.zh-TW.md b/documents/README.zh-TW.md index cb46fd6f..0c2cdd18 100644 --- a/documents/README.zh-TW.md +++ b/documents/README.zh-TW.md @@ -85,7 +85,7 @@ minipdf report.docx -o report.pdf io.github.mini-software minipdf - 0.1.2 + 0.1.3 ``` diff --git a/minipdf-java/README.md b/minipdf-java/README.md index 10d3ea9d..d42b1d53 100644 --- a/minipdf-java/README.md +++ b/minipdf-java/README.md @@ -11,7 +11,7 @@ The library is available from Maven Central: io.github.mini-software minipdf - 0.1.2 + 0.1.3 ``` @@ -40,14 +40,14 @@ Download the executable JAR with Maven: ```powershell mvn dependency:copy ` - -Dartifact=io.github.mini-software:minipdf-cli:0.1.2 ` + -Dartifact=io.github.mini-software:minipdf-cli:0.1.3 ` -DoutputDirectory=. ``` Convert a document: ```powershell -java -jar minipdf-cli-0.1.2.jar input.pptx -o output.pdf +java -jar minipdf-cli-0.1.3.jar input.pptx -o output.pdf ``` The CLI accepts `.docx`, `.xlsx`, and `.pptx` files. Run it with `--help` for diff --git a/minipdf-java/minipdf-cli/pom.xml b/minipdf-java/minipdf-cli/pom.xml index 7eecb22e..a566cadd 100644 --- a/minipdf-java/minipdf-cli/pom.xml +++ b/minipdf-java/minipdf-cli/pom.xml @@ -7,7 +7,7 @@ io.github.mini-software minipdf-java-parent - 0.1.2 + 0.1.3 minipdf-cli diff --git a/minipdf-java/minipdf-cli/src/main/java/io/github/minisoftware/minipdf/cli/MiniPdfCommand.java b/minipdf-java/minipdf-cli/src/main/java/io/github/minisoftware/minipdf/cli/MiniPdfCommand.java index c8f5303d..2c76b42f 100644 --- a/minipdf-java/minipdf-cli/src/main/java/io/github/minisoftware/minipdf/cli/MiniPdfCommand.java +++ b/minipdf-java/minipdf-cli/src/main/java/io/github/minisoftware/minipdf/cli/MiniPdfCommand.java @@ -20,7 +20,7 @@ @Command( name = "minipdf", - version = "minipdf-java 0.1.2", + version = "minipdf-java 0.1.3", description = "Convert XLSX, DOCX, and PPTX files to PDF with the Java MiniPdf engine.", mixinStandardHelpOptions = true, subcommands = MiniPdfCommand.ConvertCommand.class) diff --git a/minipdf-java/minipdf/pom.xml b/minipdf-java/minipdf/pom.xml index 0a3db79f..b56cfae7 100644 --- a/minipdf-java/minipdf/pom.xml +++ b/minipdf-java/minipdf/pom.xml @@ -7,7 +7,7 @@ io.github.mini-software minipdf-java-parent - 0.1.2 + 0.1.3 minipdf diff --git a/minipdf-java/minipdf/src/main/java/io/github/minisoftware/minipdf/internal/xlsx/PoiXlsxRenderer.java b/minipdf-java/minipdf/src/main/java/io/github/minisoftware/minipdf/internal/xlsx/PoiXlsxRenderer.java index 88cce7d7..ac39ddab 100644 --- a/minipdf-java/minipdf/src/main/java/io/github/minisoftware/minipdf/internal/xlsx/PoiXlsxRenderer.java +++ b/minipdf-java/minipdf/src/main/java/io/github/minisoftware/minipdf/internal/xlsx/PoiXlsxRenderer.java @@ -68,6 +68,7 @@ final class PoiXlsxRenderer { private static final float POINTS_PER_INCH = 72.0f; private static final float EMU_PER_POINT = 12_700.0f; private static final float CELL_PADDING = 2.0f; + private static final float VERDANA_ROW_HEIGHT_SCALE = 0.919f; private static final float CENTERED_VML_HORIZONTAL_GEOMETRY_SCALE = 1.0777f; private static final float CENTERED_VML_BORDER_SCALE = 1.8f; @@ -121,6 +122,13 @@ private static void renderSheet( PageGeometry geometry = pageGeometry(sheet, options); XSSFPrintSetup setup = sheet.getPrintSetup(); List merged = sheet.getMergedRegions(); + CellRangeAddress repeatingRows = sheet.getRepeatingRows(); + int repeatingFirstRow = repeatingRows == null + ? -1 + : Math.max(area.getFirstRow(), repeatingRows.getFirstRow()); + int repeatingLastRow = repeatingRows == null + ? -1 + : Math.min(area.getLastRow(), repeatingRows.getLastRow()); boolean hasPrintArea = printAreaValue(workbook, sheet, sheetIndex) != null; float verticalScaleLimit = explicitScale(sheet) && sheet.getVerticallyCenter() ? Math.min(1.0f, geometry.usableHeight() / rowsBefore( @@ -135,28 +143,36 @@ private static void renderSheet( group.lastColumn()); int startRow = area.getFirstRow(); while (startRow <= area.getLastRow()) { - int endRow = pageEndRow( - sheet, - startRow, - area.getLastRow(), - geometry.usableHeight(), - group.scale()); - renderPage( - document, - fonts, - workbook, - sheet, - groupArea, - merged, - group.widths(), - startRow, - endRow, - group.scale(), - geometry, - formatter, + boolean repeatTitles = repeatingFirstRow >= 0 && startRow > repeatingLastRow; + float repeatingHeight = repeatTitles + ? rowsBefore(sheet, repeatingFirstRow, repeatingLastRow + 1) * group.scale() + : 0.0f; + int endRow = pageEndRow( + sheet, + startRow, + area.getLastRow(), + geometry.usableHeight() - repeatingHeight, + group.scale()); + List pageRows = repeatTitles + ? List.of( + new RowRange(repeatingFirstRow, repeatingLastRow), + new RowRange(startRow, endRow)) + : List.of(new RowRange(startRow, endRow)); + renderPage( + document, + fonts, + workbook, + sheet, + groupArea, + merged, + group.widths(), + pageRows, + group.scale(), + geometry, + formatter, evaluator, legacyPictures); - startRow = endRow + 1; + startRow = endRow + 1; } } } @@ -169,14 +185,13 @@ private static void renderPage( CellRangeAddress area, List merged, float[] columnWidths, - int startRow, - int endRow, + List pageRows, float scale, PageGeometry geometry, DataFormatter formatter, FormulaEvaluator evaluator, List legacyPictures) throws IOException { - PDPage page = new PDPage(new PDRectangle(geometry.width(), geometry.height())); + PDPage page = new PDPage(new PDRectangle(geometry.mediaWidth(), geometry.mediaHeight())); document.addPage(page); float unscaledWidth = sum(columnWidths, 0, columnWidths.length); float horizontalScale = scale; @@ -196,28 +211,35 @@ private static void renderPage( columnWidths, geometry.marginLeft() + horizontalOffset, horizontalScale); - float usedHeight = rowsBefore(sheet, startRow, endRow + 1) * scale; + float usedHeight = (float) pageRows.stream() + .mapToDouble(rows -> rowsBefore(sheet, rows.firstRow(), rows.lastRow() + 1) * scale) + .reduce(0.0, Double::sum); float centerOffset = sheet.getVerticallyCenter() ? Math.max(0.0f, (geometry.usableHeight() - usedHeight) / 2.0f) : 0.0f; - float[] rowTop = rowPositions( - sheet, - startRow, - endRow, - geometry.height() - geometry.marginTop() - centerOffset, - scale); try (PDPageContentStream content = new PDPageContentStream(document, page)) { - for (int rowIndex = startRow; rowIndex <= endRow; rowIndex++) { - Row row = sheet.getRow(rowIndex); - float cellTop = rowTop[rowIndex - startRow]; - float cellHeight = rowHeight(sheet, rowIndex) * scale; - for (int column = area.getFirstColumn(); column <= area.getLastColumn(); column++) { - int localColumn = column - area.getFirstColumn(); - float x = columnX[localColumn]; - float width = columnWidths[localColumn] * horizontalScale; - Cell cell = row == null ? null : row.getCell(column, Row.MissingCellPolicy.RETURN_NULL_AND_BLANK); - XSSFCellStyle style = cell == null ? null : (XSSFCellStyle) cell.getCellStyle(); - CellRangeAddress merge = mergedAt(merged, rowIndex, column); + content.transform(new Matrix( + geometry.outputScale(), + 0.0f, + 0.0f, + geometry.outputScale(), + geometry.outputOffsetX(), + geometry.outputOffsetY())); + float rangeOffset = 0.0f; + for (RowRange rows : pageRows) { + float cellTop = geometry.layoutHeight() - geometry.marginTop() - centerOffset - rangeOffset; + for (int rowIndex = rows.firstRow(); rowIndex <= rows.lastRow(); rowIndex++) { + Row row = sheet.getRow(rowIndex); + float cellHeight = rowHeight(sheet, rowIndex) * scale; + for (int column = area.getFirstColumn(); column <= area.getLastColumn(); column++) { + int localColumn = column - area.getFirstColumn(); + float x = columnX[localColumn]; + float width = columnWidths[localColumn] * horizontalScale; + Cell cell = row == null + ? null + : row.getCell(column, Row.MissingCellPolicy.RETURN_NULL_AND_BLANK); + XSSFCellStyle style = cell == null ? null : (XSSFCellStyle) cell.getCellStyle(); + CellRangeAddress merge = mergedAt(merged, rowIndex, column); boolean mergeOrigin = merge != null && rowIndex == merge.getFirstRow() && column == merge.getFirstColumn(); @@ -226,9 +248,9 @@ private static void renderPage( : mergedWidth(columnWidths, area.getFirstColumn(), merge, horizontalScale); float textHeight = merge == null ? cellHeight - : mergedHeight(sheet, merge, startRow, endRow, scale); + : mergedHeight(sheet, merge, rows.firstRow(), rows.lastRow(), scale); if (merge == null || mergeOrigin) { - drawCell( + drawCell( content, style, x, @@ -237,12 +259,11 @@ private static void renderPage( textHeight, centeredLegacy ? CENTERED_VML_BORDER_SCALE : 1.0f); } - if (cell == null || (merge != null - && !mergeOrigin)) { - continue; - } - String value = formatter.formatCellValue(cell, evaluator); - drawText( + if (cell == null || (merge != null && !mergeOrigin)) { + continue; + } + String value = formatter.formatCellValue(cell, evaluator); + drawText( content, fonts, workbook, @@ -255,22 +276,25 @@ private static void renderPage( textHeight, scale, centeredLegacy ? 0.75f : CELL_PADDING); + } + cellTop -= cellHeight; } - } drawPictures( document, content, sheet, area, columnWidths, - startRow, - endRow, + rows.firstRow(), + rows.lastRow(), scale, horizontalScale, geometry, horizontalOffset, - centerOffset, + centerOffset + rangeOffset, legacyPictures); + rangeOffset += rowsBefore(sheet, rows.firstRow(), rows.lastRow() + 1) * scale; + } } } @@ -336,7 +360,7 @@ private static void drawText( } XSSFFont cellFont = workbook.getFontAt(style == null ? 0 : style.getFontIndex()); float fontSize = Math.max(4.0f, cellFont.getFontHeightInPoints() * scale); - PDFont font = fonts.resolve(cellFont.getFontName(), value, cellFont.getBold()); + PDFont font = fonts.resolve(cellFont.getFontName(), value, cellFont.getBold(), cellFont.getItalic()); String safeValue = fonts.sanitize(font, value.replace('\t', ' ').replace('\r', '\n')); List lines = style != null && style.getRotation() == 255 ? safeValue.codePoints() @@ -346,11 +370,11 @@ private static void drawText( : wrap(font, safeValue, fontSize, width - padding * 2.0f, style != null && style.getWrapText()); float lineHeight = fontSize * 1.18f; float verticalPadding = padding; - int maxLines = Math.max(1, (int) ((height - verticalPadding * 2.0f) / lineHeight)); + int maxLines = Math.max(1, 1 + (int) Math.floor((height - fontSize) / lineHeight)); if (lines.size() > maxLines) { lines = lines.subList(0, maxLines); } - float blockHeight = lines.size() * lineHeight; + float blockHeight = fontSize + Math.max(0, lines.size() - 1) * lineHeight; VerticalAlignment vertical = style == null ? VerticalAlignment.BOTTOM : style.getVerticalAlignment(); float baseline = switch (vertical) { case TOP -> y + height - verticalPadding - fontSize; @@ -362,7 +386,8 @@ private static void drawText( content.addRect(x + 0.2f, y + 0.2f, Math.max(0.1f, width - 0.4f), Math.max(0.1f, height - 0.4f)); content.clip(); content.setNonStrokingColor(textColor); - if (cellFont.getBold()) { + if (cellFont.getBold() + && fonts.requiresSyntheticBold(cellFont.getFontName(), value, cellFont.getItalic())) { content.setStrokingColor(textColor); content.setLineWidth(Math.max(0.15f, fontSize * 0.025f)); content.setRenderingMode(RenderingMode.FILL_STROKE); @@ -417,7 +442,7 @@ private static void drawPictures( float x = geometry.marginLeft() + horizontalOffset + columnsBefore(columnWidths, area.getFirstColumn(), anchor.getCol1()) * horizontalScale + anchor.getDx1() / EMU_PER_POINT * horizontalScale; - float top = geometry.height() - geometry.marginTop() - centerOffset + float top = geometry.layoutHeight() - geometry.marginTop() - centerOffset - rowsBefore(sheet, startRow, anchor.getRow1()) * scale - anchor.getDy1() / EMU_PER_POINT * scale; float width = (columnsBefore(columnWidths, area.getFirstColumn(), anchor.getCol2()) @@ -476,7 +501,7 @@ private static void drawLegacyPicture( float x = geometry.marginLeft() + horizontalOffset + columnsBefore(columnWidths, area.getFirstColumn(), column1) * horizontalScale + columnWidth(sheet, column1) * anchor[1] / 1024.0f * horizontalScale; - float top = geometry.height() - geometry.marginTop() - centerOffset + float top = geometry.layoutHeight() - geometry.marginTop() - centerOffset - rowsBefore(sheet, startRow, row1) * scale - rowHeight(sheet, row1) * anchor[3] / 256.0f * scale; float width = (columnsBefore(columnWidths, area.getFirstColumn(), column2) @@ -585,27 +610,46 @@ private static boolean referencesSheet(String formula, String sheetName) { private static PageGeometry pageGeometry(XSSFSheet sheet, ConversionOptions options) { PageSize configured = options.pageSize().orElse(null); - float width; - float height; + float layoutWidth; + float layoutHeight; + float mediaWidth; + float mediaHeight; if (configured != null) { - width = configured.width(); - height = configured.height(); + layoutWidth = configured.width(); + layoutHeight = configured.height(); + mediaWidth = layoutWidth; + mediaHeight = layoutHeight; } else { PrintSetup setup = sheet.getPrintSetup(); + boolean explicitPaperSize = sheet.getCTWorksheet().isSetPageSetup() + && sheet.getCTWorksheet().getPageSetup().isSetPaperSize(); boolean letter = setup.getPaperSize() == PrintSetup.LETTER_PAPERSIZE; - width = letter ? PageSize.LETTER.width() : PageSize.A4.width(); - height = letter ? PageSize.LETTER.height() : PageSize.A4.height(); + layoutWidth = letter ? PageSize.LETTER.width() : PageSize.A4.width(); + layoutHeight = letter ? PageSize.LETTER.height() : PageSize.A4.height(); + mediaWidth = explicitPaperSize ? layoutWidth : PageSize.A4.width(); + mediaHeight = explicitPaperSize ? layoutHeight : PageSize.A4.height(); if (setup.getLandscape()) { - float swap = width; - width = height; - height = swap; + float layoutSwap = layoutWidth; + layoutWidth = layoutHeight; + layoutHeight = layoutSwap; + float mediaSwap = mediaWidth; + mediaWidth = mediaHeight; + mediaHeight = mediaSwap; } } float left = margin(sheet.getMargin(PageMargin.LEFT), 0.25f); float right = margin(sheet.getMargin(PageMargin.RIGHT), 0.25f); float top = margin(sheet.getMargin(PageMargin.TOP), 0.3f); float bottom = margin(sheet.getMargin(PageMargin.BOTTOM), 0.3f); - return new PageGeometry(width, height, left, right, top, bottom); + return new PageGeometry( + layoutWidth, + layoutHeight, + mediaWidth, + mediaHeight, + left, + right, + top, + bottom); } private static float margin(double inches, float fallback) { @@ -718,7 +762,18 @@ private static int pageEndRow(XSSFSheet sheet, int startRow, int lastRow, float private static float rowHeight(XSSFSheet sheet, int rowIndex) { Row row = sheet.getRow(rowIndex); - return row == null || row.getZeroHeight() ? sheet.getDefaultRowHeightInPoints() : row.getHeightInPoints(); + float height = row == null || row.getZeroHeight() + ? sheet.getDefaultRowHeightInPoints() + : row.getHeightInPoints(); + if (row != null) { + for (Cell cell : row) { + XSSFFont font = sheet.getWorkbook().getFontAt(cell.getCellStyle().getFontIndex()); + if ("verdana".equalsIgnoreCase(font.getFontName())) { + return height * VERDANA_ROW_HEIGHT_SCALE; + } + } + } + return height; } private static CellRangeAddress mergedAt(List merged, int row, int column) { @@ -814,7 +869,7 @@ private static Color color(XSSFColor source, Color fallback) { if (source == null) { return fallback; } - byte[] rgb = source.getRGB(); + byte[] rgb = source.getRGBWithTint(); if (rgb == null || rgb.length < 3) { byte[] argb = source.getARGB(); if (argb == null || argb.length < 4) { @@ -826,27 +881,48 @@ private static Color color(XSSFColor source, Color fallback) { } private record PageGeometry( - float width, - float height, + float layoutWidth, + float layoutHeight, + float mediaWidth, + float mediaHeight, float marginLeft, float marginRight, float marginTop, float marginBottom) { float usableWidth() { - return width - marginLeft - marginRight; + return layoutWidth - marginLeft - marginRight; } float usableHeight() { - return height - marginTop - marginBottom; + return layoutHeight - marginTop - marginBottom; + } + + float outputScale() { + return Math.min(mediaWidth / layoutWidth, mediaHeight / layoutHeight); + } + + float outputOffsetX() { + return (mediaWidth - layoutWidth * outputScale()) / 2.0f; + } + + float outputOffsetY() { + return (mediaHeight - layoutHeight * outputScale()) / 2.0f; } } private record ColumnGroup(int firstColumn, int lastColumn, float[] widths, float scale) { } + private record RowRange(int firstRow, int lastRow) { + } + private static final class FontSet { private final PDFont latin; private final PDFont latinBold; + private final PDFont verdana; + private final PDFont verdanaBold; + private final PDFont verdanaItalic; + private final PDFont verdanaBoldItalic; private final PDFont calibri; private final PDFont calibriBold; private final PDFont times; @@ -860,6 +936,10 @@ private static final class FontSet { private FontSet( PDFont latin, PDFont latinBold, + PDFont verdana, + PDFont verdanaBold, + PDFont verdanaItalic, + PDFont verdanaBoldItalic, PDFont calibri, PDFont calibriBold, PDFont times, @@ -871,6 +951,10 @@ private FontSet( PDFont kaiti) { this.latin = latin; this.latinBold = latinBold; + this.verdana = verdana; + this.verdanaBold = verdanaBold; + this.verdanaItalic = verdanaItalic; + this.verdanaBoldItalic = verdanaBoldItalic; this.calibri = calibri; this.calibriBold = calibriBold; this.times = times; @@ -889,6 +973,14 @@ static FontSet load(PDDocument document) throws IOException { } PDFont latin = load(document, registered, List.of("arial"), systemFonts("arial.ttf")); PDFont latinBold = load(document, registered, List.of("arialbd"), systemFonts("arialbd.ttf")); + PDFont verdana = load(document, registered, List.of("verdana"), systemFonts("verdana.ttf")); + PDFont verdanaBold = load(document, registered, List.of("verdanab"), systemFonts("verdanab.ttf")); + PDFont verdanaItalic = load(document, registered, List.of("verdanai"), systemFonts("verdanai.ttf")); + PDFont verdanaBoldItalic = load( + document, + registered, + List.of("verdanaz"), + systemFonts("verdanaz.ttf")); PDFont calibri = load(document, registered, List.of("calibri"), systemFonts("calibri.ttf")); PDFont calibriBold = load( document, @@ -913,6 +1005,18 @@ static FontSet load(PDDocument document) throws IOException { if (latinBold == null) { latinBold = new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD); } + if (verdana == null) { + verdana = latin; + } + if (verdanaBold == null) { + verdanaBold = latinBold; + } + if (verdanaItalic == null) { + verdanaItalic = verdana; + } + if (verdanaBoldItalic == null) { + verdanaBoldItalic = verdanaBold; + } if (calibri == null) { calibri = latin; } @@ -941,6 +1045,10 @@ static FontSet load(PDDocument document) throws IOException { return new FontSet( latin, latinBold, + verdana, + verdanaBold, + verdanaItalic, + verdanaBoldItalic, calibri, calibriBold, times, @@ -952,7 +1060,7 @@ static FontSet load(PDDocument document) throws IOException { kaiti); } - PDFont resolve(String requested, String text, boolean bold) { + PDFont resolve(String requested, String text, boolean bold, boolean italic) { String name = requested == null ? "" : requested.toLowerCase(Locale.ROOT); if (name.contains("kaiti") || name.contains("kai") || name.contains("楷")) { return kaiti; @@ -966,6 +1074,15 @@ PDFont resolve(String requested, String text, boolean bold) { if (text.codePoints().anyMatch(codePoint -> codePoint > 255)) { return bold ? cjkBold : cjk; } + if (name.contains("verdana")) { + if (bold && italic) { + return verdanaBoldItalic; + } + if (italic) { + return verdanaItalic; + } + return bold ? verdanaBold : verdana; + } if (name.contains("calibri")) { return bold ? calibriBold : calibri; } @@ -975,6 +1092,10 @@ PDFont resolve(String requested, String text, boolean bold) { return bold ? latinBold : latin; } + boolean requiresSyntheticBold(String requested, String text, boolean italic) { + return resolve(requested, text, true, italic) == resolve(requested, text, false, italic); + } + String sanitize(PDFont font, String value) throws IOException { if (!(font instanceof PDType0Font)) { return value.chars() diff --git a/minipdf-java/minipdf/src/test/java/io/github/minisoftware/minipdf/ClassicFixtureSmokeTest.java b/minipdf-java/minipdf/src/test/java/io/github/minisoftware/minipdf/ClassicFixtureSmokeTest.java index 3621a8c8..7075c575 100644 --- a/minipdf-java/minipdf/src/test/java/io/github/minisoftware/minipdf/ClassicFixtureSmokeTest.java +++ b/minipdf-java/minipdf/src/test/java/io/github/minisoftware/minipdf/ClassicFixtureSmokeTest.java @@ -18,6 +18,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ClassicFixtureSmokeTest { @@ -33,6 +34,22 @@ void convertsTrackedXlsxFixture() throws Exception { assertTrue(pdf.contains("Wedding florals")); } + @Test + void defaultsUnspecifiedXlsxPaperSizeToA4() throws Exception { + Path fixture = REPOSITORY_ROOT.resolve("tests/Issue_Files/xlsx/XlsxIssue77_Template1.xlsx"); + + try (PDDocument document = Loader.loadPDF(MiniPdf.convertToPdfBytes(fixture))) { + assertEquals(PageSize.A4.width(), document.getPage(0).getMediaBox().getWidth(), 0.1f); + assertEquals(PageSize.A4.height(), document.getPage(0).getMediaBox().getHeight(), 0.1f); + assertEquals(6, document.getNumberOfPages()); + PDFTextStripper stripper = new PDFTextStripper(); + stripper.setStartPage(2); + stripper.setEndPage(2); + String secondPage = stripper.getText(document); + assertTrue(secondPage.contains("RMA AUTHORIZATION"), secondPage); + } + } + @Test void convertsIssueXlsxWithCjkText() throws Exception { Path fixture = REPOSITORY_ROOT.resolve("tests/Issue_Files/xlsx/Issue202609031340.xlsx"); diff --git a/minipdf-java/pom.xml b/minipdf-java/pom.xml index 40ec7fac..c98ad043 100644 --- a/minipdf-java/pom.xml +++ b/minipdf-java/pom.xml @@ -6,7 +6,7 @@ io.github.mini-software minipdf-java-parent - 0.1.2 + 0.1.3 pom MiniPdf for Java