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
| Case | Comparison |
- Issue202609031340 format: xlsx | case: Issue202609031340 | scope: java-issue-xlsx Page 1 |
-  |
+ XlsxIssue77_Template1 format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx Page 1 |
+  |
- Issue202609031340 format: xlsx | case: Issue202609031340 | scope: java-issue-xlsx Page 2 |
-  |
+ XlsxIssue77_Template1 format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx Page 2 |
+  |
- Issue202609031340 format: xlsx | case: Issue202609031340 | scope: java-issue-xlsx Page 3 |
-  |
+ XlsxIssue77_Template1 format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx Page 3 |
+  |
- Issue202609031340 format: xlsx | case: Issue202609031340 | scope: java-issue-xlsx Page 4 |
-  |
+ XlsxIssue77_Template1 format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx Page 4 |
+  |
+
+
+ XlsxIssue77_Template1 format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx Page 5 |
+  |
+
+
+ XlsxIssue77_Template1 format: xlsx | case: XlsxIssue77_Template1 | scope: java-issue-xlsx Page 6 |
+  |
@@ -39,24 +47,34 @@ Blue areas are below the configured difference threshold; red areas have stronge
| Case | Heatmap | Metrics |
- Issue202609031340 Page 1 |
-  |
- changed: 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 |
+  |
+ changed: 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 |
-  |
- changed: 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 |
+  |
+ changed: 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 |
-  |
- changed: 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 |
+  |
+ changed: 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 |
-  |
- changed: 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 |
+  |
+ changed: 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 |
+  |
+ changed: 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 |
+  |
+ changed: 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 MiniPdf | Microsoft 365 Excel Reference | LibreOffice |
- 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% |
+
+
+  |
+  |
+  |
-  |
-  |
-  |
+  |
+  |
+  |
-  |
-  |
-  |
+  |
+  |
+  |
-  |
-  |
-  |
+  |
+  |
+  |
-  |
-  |
-  |
+  |
+  |
+  |
+
+
+  |
+  |
+  |
## 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