Skip to content
Wei Lin edited this page Mar 5, 2026 · 3 revisions

Benchmark — Self-Evolution Pipeline

MiniPdf uses a self-evolution benchmark pipeline to measure output quality against LibreOffice as the reference renderer across 150 classic Excel scenarios.


Pipeline Architecture

generate_classic_xlsx.py → 120 .xlsx files
│
├──► dotnet (MiniPdf) → ./pdf_output/classic*.pdf
└──► LibreOffice → ./reference_pdfs/classic*.pdf
│
compare_pdfs.py
│
reports/comparison_report.{md,json,html}

Scoring Formula

ComponentWeightMethod
Text similarity40%difflib.SequenceMatcher on per-page extracted text
Visual similarity40%Pixel-level diff via PyMuPDF rendering (150 DPI)
Page count match20%1.0 if equal, min/max ratio otherwise

Overall score = text × 0.4 + visual × 0.4 + pages × 0.2


Running the Benchmark

Prerequisites

pip install openpyxl pymupdf
# LibreOffice installed (soffice on PATH)# .NET 9 SDK

Full Pipeline

# Windows (UTF-8 output)$env:PYTHONIOENCODING="utf-8"
python tests/MiniPdf.Benchmark/run_benchmark.py

Partial Runs

# Skip Excel generation (xlsx already exist)
python tests/MiniPdf.Benchmark/run_benchmark.py --skip-generate# Skip both generation and LibreOffice reference (only re-run MiniPdf + compare)
python tests/MiniPdf.Benchmark/run_benchmark.py --skip-generate--skip-reference# Only compare (assumes all PDFs already exist)
python tests/MiniPdf.Benchmark/run_benchmark.py --compare-only

The report is written to tests/MiniPdf.Benchmark/reports/:

  • comparison_report.md — human-readable Markdown with diffs
  • comparison_report.json — machine-readable scores
  • comparison_report.html — visual side-by-side page images
  • images/ — PNG renders of each PDF page (minipdf + reference)

Current Results (2026-03-05)

Average overall score: 97.1% across 150 test cases (100 Excellent, 22 Good, 16 Acceptable, 9 Fair, 3 Needs Improvement).

Test cases classic61–classic90 include image scenarios, classic91–classic120 include chart rendering scenarios, and classic121–classic150 include style/border/fill scenarios.

#Test CaseTextVisualPagesOverall
1classic01 basic table with headers1.0000.9971/1🟢 99.9%
2classic02 multiple worksheets0.9970.9983/3🟢 99.8%
3classic03 empty workbook1.0001.0001/1🟢 100.0%
4classic04 single cell1.0001.0001/1🟢 100.0%
5classic05 wide table (A–Z)1.0000.9943/3🟢 99.7%
6classic06 tall table (200 rows)1.0000.9455/5🟡 97.8%
7classic07 numbers only1.0000.9991/1🟢 100.0%
8classic08 mixed text and numbers1.0000.9981/1🟢 99.9%
9classic09 long text0.6750.5777/12🔴 60.1%
10classic10 special XML characters1.0000.9971/1🟢 99.9%
11classic11 sparse rows1.0000.9992/2🟢 100.0%
12classic12 sparse columns1.0000.9981/1🟢 99.9%
13classic13 date strings0.9740.9961/1🟡 98.8%
14classic14 decimal numbers1.0000.9971/1🟢 99.9%
15classic15 negative numbers1.0000.9971/1🟢 99.9%
16classic16 percentage strings0.9940.9971/1🟢 99.6%
17classic17 currency strings1.0000.9961/1🟢 99.9%
18classic18 large dataset (1000×10)1.0000.89324/24🟡 95.7%
19classic19 single column list1.0000.9971/1🟢 99.9%
20classic20 all empty cells1.0001.0001/1🟢 100.0%
21classic21 header only1.0000.9991/1🟢 100.0%
22classic22 long sheet name1.0000.9991/1🟢 99.9%
23classic23 unicode / CJK text0.7920.9951/1🟡 91.5%
24classic24 red text1.0000.9961/1🟢 99.9%
25classic25 multiple colors1.0000.9951/1🟢 99.8%
26classic26 inline strings1.0000.9971/1🟢 99.9%
27classic27 single row1.0000.9991/1🟢 99.9%
28classic28 duplicate values1.0000.9971/1🟢 99.9%
29classic29 formula results1.0000.9971/1🟢 99.9%
30classic30 mixed empty & filled sheets1.0000.9992/2🟢 100.0%
31classic31 bold header row1.0000.9961/1🟢 99.8%
32classic32 right-aligned numbers1.0000.9971/1🟢 99.9%
33classic33 centered text1.0000.9981/1🟢 99.9%
34classic34 explicit column widths1.0000.9961/1🟢 99.8%
35classic35 explicit row heights1.0000.9991/1🟢 100.0%
36classic36 merged cells1.0000.9961/1🟢 99.8%
37classic37 freeze panes1.0000.9891/1🟢 99.6%
38classic38 hyperlink cell1.0000.9971/1🟢 99.9%
39classic39 financial table1.0000.9941/1🟢 99.8%
40classic40 scientific notation1.0000.9961/1🟢 99.9%
41classic41 integer vs float1.0000.9971/1🟢 99.9%
42classic42 boolean values0.9900.9961/1🟢 99.4%
43classic43 inventory report0.9980.9891/1🟢 99.5%
44classic44 employee roster0.9970.9841/1🟢 99.2%
45classic45 sales by region (4 sheets)1.0000.9984/4🟢 99.9%
46classic46 grade book1.0000.9911/1🟢 99.6%
47classic47 time series (31 rows)1.0000.9821/1🟢 99.3%
48classic48 survey results0.9940.9931/1🟢 99.5%
49classic49 contact list0.9460.9891/1🟡 97.4%
50classic50 budget vs actuals (3 sheets)1.0000.9903/3🟢 99.6%
51classic51 product catalog0.9780.9861/1🟡 98.6%
52classic52 pivot summary1.0000.9901/1🟢 99.6%
53classic53 invoice layout0.9950.9911/1🟢 99.5%
54classic54 multi-level header1.0000.9941/1🟢 99.8%
55classic55 error values1.0000.9951/1🟢 99.8%
56classic56 alternating row colors1.0000.9911/1🟢 99.6%
57classic57 CJK-only sheet0.7830.9081/1🔴 87.6%
58classic58 mixed numeric formats0.9940.9961/1🟢 99.6%
59classic59 multi-sheet summary (4 sheets)1.0000.9964/4🟢 99.9%
60classic60 large wide table (20×50)1.0000.9344/4🟡 97.4%
61classic61 product card with image1.0000.9981/1🟢 99.9%
62classic62 company logo header1.0000.9951/1🟢 99.8%
63classic63 two products side by side1.0000.9951/1🟢 99.8%
64classic64 employee directory with photo0.9970.9931/1🟢 99.6%
65classic65 inventory with product photos0.9910.9941/1🟢 99.4%
66classic66 invoice with logo0.9870.9951/1🟢 99.3%
67classic67 real estate listing1.0000.9941/1🟢 99.8%
68classic68 restaurant menu0.9780.9811/1🟡 98.4%
69classic69 image only sheet1.0000.9991/1🟢 100.0%
70classic70 product catalog with images0.9830.9931/1🟢 99.0%
71classic71 multi sheet with images1.0000.9993/3🟢 100.0%
72classic72 bar chart image with data1.0000.9881/1🟢 99.5%
73classic73 event flyer with banner0.9450.9931/1🟡 97.5%
74classic74 dashboard with KPI image0.9630.9891/1🟡 98.0%
75classic75 certificate with seal1.0000.9881/1🟢 99.5%
76classic76 product image grid1.0000.9901/1🟢 99.6%
77classic77 news article with hero image1.0000.9901/1🟢 99.6%
78classic78 small icon per row0.9900.9951/1🟢 99.4%
79classic79 wide panoramic banner1.0000.9941/1🟢 99.8%
80classic80 portrait tall image1.0000.9941/1🟢 99.8%
81classic81 step by step with images1.0000.9911/1🟢 99.7%
82classic82 before after images0.9930.9921/1🟢 99.4%
83classic83 color swatch palette0.9920.9921/1🟢 99.4%
84classic84 travel destination cards1.0000.9891/1🟢 99.6%
85classic85 lab results with image0.9930.9891/1🟢 99.3%
86classic86 software screenshot features0.9860.9941/1🟢 99.2%
87classic87 sports results with logos1.0000.9951/1🟢 99.8%
88classic88 image after data1.0000.9941/1🟢 99.8%
89classic89 nutrition label with image0.9980.9941/1🟢 99.7%
90classic90 project status with milestones0.9590.9871/1🟡 97.8%
91classic91 simple bar chart0.9490.9612/2🟡 96.4%
92classic92 horizontal bar chart0.9710.9672/2🟡 97.5%
93classic93 line chart0.8260.9862/2🟡 92.5%
94classic94 pie chart0.8780.9262/2🟡 92.1%
95classic95 area chart0.6440.7652/2🔴 76.4%
96classic96 scatter chart0.8920.9862/2🟡 95.1%
97classic97 doughnut chart0.8570.9322/2🟡 91.5%
98classic98 radar chart0.8880.9902/2🟡 95.1%
99classic99 bubble chart0.8450.9642/2🟡 92.3%
100classic100 stacked bar chart0.9660.9051/1🟡 94.9%
101classic101 percent stacked bar0.9620.8781/1🟡 93.6%
102classic102 line chart with markers0.8610.9882/2🟡 94.0%
103classic103 pie chart with labels0.7370.9752/2🔴 88.4%
104classic104 combo bar line chart0.7870.7532/2🔴 81.6%
105classic105 3D bar chart0.9030.7442/2🔴 85.9%
106classic106 3D pie chart0.8240.9682/2🟡 91.7%
107classic107 multi series line0.7380.7772/2🔴 80.6%
108classic108 stacked area chart0.9640.8961/1🟡 94.4%
109classic109 scatter with trendline0.8290.9862/2🟡 92.6%
110classic110 chart with legend0.8370.7812/2🔴 84.7%
111classic111 chart with axis labels0.8270.9772/2🟡 92.1%
112classic112 multiple charts0.8750.7592/2🔴 85.4%
113classic113 chart sheet0.9260.7352/2🔴 86.4%
114classic114 chart large dataset0.9010.8874/4🟡 91.5%
115classic115 chart negative values0.8320.9722/2🟡 92.2%
116classic116 percent stacked area0.9650.8801/1🟡 93.8%
117classic117 stock OHLC chart0.7940.7292/2🔴 80.9%
118classic118 bar chart custom colors0.9270.9612/2🟡 95.5%
119classic119 dashboard multi charts0.8410.9352/2🟡 91.0%
120classic120 chart with date axis0.5740.7832/2🔴 74.3%
121classic121 thin borders1.0000.9931/1🟢 99.7%
122classic122 thick outer thin inner1.0000.9911/1🟢 99.6%
123classic123 dashed borders0.9920.9951/1🟢 99.5%
124classic124 colored borders1.0000.9931/1🟢 99.7%
125classic125 solid fills0.9920.9931/1🟢 99.4%
126classic126 dark header0.9930.9911/1🟢 99.4%
127classic127 font styles0.9910.9921/1🟢 99.3%
128classic128 font sizes0.9820.9941/1🟢 99.0%
129classic129 alignment combos1.0000.9961/1🟢 99.9%
130classic130 wrap and indent1.0000.9871/1🟢 99.5%
131classic131 number formats1.0000.9911/1🟢 99.7%
132classic132 striped table1.0000.9781/1🟢 99.1%
133classic133 gradient rows1.0000.9911/1🟢 99.7%
134classic134 heatmap1.0000.9711/1🟡 98.9%
135classic135 bottom border only1.0000.9961/1🟢 99.8%
136classic136 financial report styled1.0000.9831/1🟢 99.3%
137classic137 checkerboard1.0000.9601/1🟡 98.4%
138classic138 color grid1.0000.9861/1🟢 99.5%
139classic139 pattern fills1.0000.9811/1🟢 99.2%
140classic140 rotated text0.9580.9941/1🟡 98.1%
141classic141 mixed edge borders1.0000.9931/1🟢 99.7%
142classic142 styled invoice1.0000.9591/1🟡 98.4%
143classic143 colored tabs1.0000.9994/4🟢 100.0%
144classic144 note style cells1.0000.9891/1🟢 99.6%
145classic145 status badges1.0000.9771/1🟢 99.1%
146classic146 double border table1.0000.9891/1🟢 99.6%
147classic147 multi sheet styled1.0000.9933/3🟢 99.7%
148classic148 frozen styled grid1.0000.9341/1🟡 97.4%
149classic149 merged styled sections1.0000.9681/1🟡 98.7%
150classic150 kitchen sink styles0.9680.9691/1🟡 97.5%

Known Limitations

IssueAffected CasesRoot Cause
Long text not paginatedclassic09 (60.1%)MiniPdf renders as clipped single line; LibreOffice paginates with text wrapping
CJK / Unicode glyphsclassic23 (91.5%), classic57 (87.6%)Helvetica (built-in PDF font) does not include CJK code points; width estimation differs
Chart text/layout31 chart cases (74–96%)Y-axis scale, legend placement, title clipping differ from LibreOffice's Calc charting engine
No 3-D chart effectsclassic105, classic1063-D chart styles rendered as flat 2-D equivalents
Unsupported chart typesstock, comboFall back to basic bar renderer
Pattern fill precisionclassic137 (98.4%)Checkerboard/hatch patterns approximated via solid fill blending

Adding New Test Cases

  1. Add the generator in tests/MiniPdf.Scripts/generate_classic_xlsx.py:

    defclassic121_my_new_scenario():
    wb=Workbook()
    ws=wb.activews.append(["Col1", "Col2"])
    ws.append(["val1", "val2"])
    save(wb, "classic121_my_new_scenario.xlsx")

    Register it in main()generators list.

  2. Add the C# test in tests/MiniPdf.Tests/ClassicExcelToPdfTests.cs:

    [Fact]publicvoidClassic121_MyNewScenario(){usingvarxlsx=XlsxBuilder.Simple(new[]{"Col1","Col2"},new[]{"val1","val2"});AssertValidPdf(xlsx,"Col1","val1");}
  3. Run the pipeline:

    $env:PYTHONIOENCODING="utf-8"
    python tests/MiniPdf.Benchmark/run_benchmark.py
  4. Check the report in tests/MiniPdf.Benchmark/reports/comparison_report.md.


Self-Evolution Loop

Run Benchmark
│
▼
Read report → find low-score cases
│
▼
AI fixes ExcelToPdfConverter.cs
│
▼
dotnet test (94 tests must pass)
│
▼
Re-run benchmark (--skip-generate --skip-reference)
│
▼
Score improved? → commit. Otherwise → iterate.

Clone this wiki locally