Line Wrapping Benchmark
This benchmark tests the performance of line wrapping and simple general layouting. The Project Gutenberg text of Homer’s Odyssey is used for this purposes.
Benchmark Setup
The text of the Odyssey is arranged on pages of the dimension WIDTH
x1000 where WIDTH
is set to
different values (400, 200, 100 and 50 by default). Additionally, all widths are combined once with
the standard PDF Type1 font Times-Roman and once with a TrueType font (DejaVu Sans by default).
In the case of pages with a width of 400 no line wrapping needs to be done because each line in the source text is shorter than 400 points. In the other cases lines need to be actually wrapped and the number of pages increases. With a width of 50 even words need sometimes to be broken.
Each benchmark script can be invoked standalone in the following way: script-executable TXT_FILE
WIDTH OUTPUT_FILE [TTF_FILE]
.
The performance of the libraries hugely depends on how the input text is provided: Some are very fast when processing the whole input file at once, others only when processing the input line by line. The fastest method was always chosen.
The list of the benchmarked libraries:
- HexaPDF
-
Homepage: http://hexapdf.gettalong.org
Language: Ruby
Version: Latest versionHexaPDF works faster if the whole input is given at once but still has acceptable runtimes for line by line input.
Two different ways of general layouting are benchmarked:
- L
- This version uses the low-level layouting facility HexaPDF::Layout::TextLayouter.
- C
- This version uses the high-level HexaPDF::Composer to construct the document.
- Prawn
-
Homepage: https://prawnpdf.org
Language: Ruby
Version: 2.4.0Prawn is much faster and uses much less memory if the input is provided line by line. However, it still works if the whole input is provided at once.
- ReportLab
-
Homepage: https://www.reportlab.com/opensource/
Language: Python
Version: 3.6.12ReportLab also needs its input line by line. Otherwise it is much, much slower (at least 60x, then the test run was aborted).
- fpdf2
-
Homepage: https://pyfpdf.github.io/fpdf2/
Language: Python
Version: 2.7.4As with HexaPDF itself, fpdf2 works equally fine when getting the input as one big string or line by line.
- TCPDF
-
Homepage: https://tcpdf.org/
Language: PHP
Version: 6.6.2As with Prawn and ReportLab, TCPDF needs its input line by line. Otherwise it is much, much slower when line wrapping needs to be done (the test run was aborted because it took too long).
Results
These benchmark results are from 2023-08-03.
Time | Memory | File size | ||
---|---|---|---|---|
hexapdf L | 400 | 872ms | 100.132KiB | 361.571 |
hexapdf C | 400 | 915ms | 93.360KiB | 361.577 |
prawn | 400 | 3.659ms | 53.820KiB | 526.289 |
reportlab/C | 400 | 1.024ms | 44.376KiB | 486.548 |
fpdf2 | 400 | 1.528ms | 65.940KiB | 436.467 |
tcpdf | 400 | 1.013ms | 33.880KiB | 513.780 |
hexapdf L | 200 | 942ms | 105.456KiB | 408.488 |
hexapdf C | 200 | 968ms | 96.236KiB | 408.487 |
prawn | 200 | 4.846ms | 56.428KiB | 665.500 |
reportlab/C | 200 | 1.109ms | 44.816KiB | 584.702 |
fpdf2 | 200 | 1.722ms | 69.776KiB | 545.185 |
tcpdf | 200 | 1.147ms | 35.412KiB | 668.559 |
hexapdf L | 100 | 1.066ms | 109.812KiB | 463.815 |
hexapdf C | 100 | 1.116ms | 100.768KiB | 463.809 |
prawn | 100 | 6.517ms | 63.120KiB | 850.581 |
reportlab/C | 100 | 1.220ms | 45.656KiB | 698.375 |
fpdf2 | 100 | 2.048ms | 75.656KiB | 687.194 |
tcpdf | 100 | 1.443ms | 38.232KiB | 918.499 |
hexapdf L | 50 | 1.676ms | 206.016KiB | 569.338 |
hexapdf C | 50 | 1.649ms | 212.212KiB | 569.338 |
prawn | 50 | 11.087ms | 69.352KiB | 1.263.210 |
reportlab/C | 50 | 1.413ms | 47.328KiB | 933.885 |
fpdf2 | 50 | 2.615ms | 91.016KiB | 1.006.761 |
tcpdf | 50 | 2.132ms | 46.164KiB | 1.465.888 |
hexapdf L | 400 ttf | 967ms | 105.736KiB | 442.425 |
hexapdf C | 400 ttf | 1.007ms | 101.760KiB | 442.438 |
prawn | 400 ttf | 3.459ms | 54.048KiB | 561.034 |
reportlab/C | 400 ttf | 1.112ms | 48.944KiB | 621.389 |
fpdf2 | 400 ttf | 1.770ms | 67.216KiB | 523.438 |
tcpdf | 400 ttf | 1.228ms | 39.252KiB | 631.677 |
hexapdf L | 200 ttf | 1.002ms | 98.456KiB | 504.454 |
hexapdf C | 200 ttf | 1.015ms | 98.560KiB | 504.454 |
prawn | 200 ttf | 4.560ms | 59.592KiB | 715.069 |
reportlab/C | 200 ttf | 1.193ms | 49.928KiB | 730.741 |
fpdf2 | 200 ttf | 1.956ms | 71.116KiB | 650.491 |
tcpdf | 200 ttf | 1.325ms | 41.348KiB | 818.349 |
hexapdf L | 100 ttf | 1.173ms | 111.944KiB | 606.547 |
hexapdf C | 100 ttf | 1.200ms | 104.832KiB | 606.543 |
prawn | 100 ttf | 6.907ms | 69.844KiB | 1.015.736 |
reportlab/C | 100 ttf | 1.342ms | 50.880KiB | 920.045 |
fpdf2 | 100 ttf | 2.373ms | 80.680KiB | 888.224 |
tcpdf | 100 ttf | 1.765ms | 44.372KiB | 1.200.745 |
hexapdf L | 50 ttf | 3.044ms | 288.980KiB | 767.835 |
hexapdf C | 50 ttf | 3.116ms | 284.808KiB | 767.852 |
prawn | 50 ttf | 11.155ms | 70.432KiB | 1.572.100 |
reportlab/C | 50 ttf | 1.668ms | 52.908KiB | 1.249.287 |
fpdf2 | 50 ttf | 3.310ms | 100.632KiB | 1.320.952 |
tcpdf | 50 ttf | 2.604ms | 54.100KiB | 1.941.055 |