Repository navigation
RayoMD 4.3.0 Text 2026 10 13
4.3.0 changes how RayoMD writes text that the standard PDF fonts cannot show, and how it draws plain code blocks. Unicode PDFs come out up to 26 % smaller and export up to 12 % faster; text and pixels are unchanged. How each number was taken is in Performance History, section Text path, October 2026.
Setup: AMD Ryzen 5 PRO 4650GE, Linux x86-64 in Docker, g++ 13.3, files on the host's /tmp,
one pinned CPU core. A one-shot export is a fresh rayomd --export process, start to exit,
median of 41 runs; warm numbers are tools/benchmark.py ab (--bench in one process, seven
rounds, with an A/A control of two identical binaries).
-
Two-byte CID strings. The Unicode renderer embeds a TrueType font as a CID font whose
CIDs are the code points. It wrote each CID as four hex digits (
<0041>); it now writes literal strings of two bytes a CID, high byte first.(,)and\take a backslash, CR is written as\r(a reader takes a bare CR in a literal string for LF), and NUL and LF stand for themselves. One table lookup tests a CID below U+0D00, where no high byte needs a backslash, and two lookups test the others. A CID without an escape is one 16-bit store. - Plain code blocks in place. A code block without a highlighted language was split into a vector of lines, and each line wrapped into a vector of strings (in the Unicode renderer also decoded from UTF-8 line by line). Both renderers now read lines and wrapped pieces where they are, as highlighted blocks already did, with the same sums in the same order and so the same line breaks.
| Document | 4.2.2 | 4.3.0 | Change | PDF size |
|---|---|---|---|---|
docs/development/performance.md (Unicode, 2,000 lines) |
5.07 ms | 4.77 ms | -6.0 % | 960,447 → 708,707 B (-26.2 %) |
README.md |
2.41 ms | 2.33 ms | -3.4 % | 252,185 → 206,091 B (-18.3 %) |
tester.md without images |
1.90 ms | 1.89 ms | -0.5 % | 81,746 → 75,253 B (-7.9 %) |
| 35 KB of C++ in code blocks, Polish comments | 2.34 ms | 2.06 ms | -12.1 % | 286,717 → 217,395 B (-24.2 %) |
| the same code, ASCII only | 1.63 ms | 1.48 ms | -9.5 % | 145,756 B, the same bytes |
| short ASCII note | 0.93 ms | 0.93 ms | -0.6 % | 1,348 B, the same bytes |
With --compress: performance.md 201,665 → 184,031 B (-8.7 %), README.md 71,872 → 68,714 B
(-4.4 %).
| Comparison | Time | Instructions |
|---|---|---|
| Two-byte CID strings, watch suite and long documents | +0.12 % (A/A -0.17 %) | -1.10 % (Unicode -0.7 to -2.3 %) |
| Plain code blocks, 30 blocks of C++ | -42.4 % | -44.7 % |
| Plain code blocks, the same with Polish comments | -34.3 % | -22.9 % |
| Plain code blocks, watch suite | +0.85 % (A/A +0.23 %) | same |
The watch suite's +0.85 % after the code-block change is code placement: no function but the
two RenderCodes changed size, and with -falign-functions=64 -falign-loops=32 the suite is
+0.01 % (A/A -0.42 %).
- 650 PDFs of 130 documents (generated text full of escapes, Braille and U+29xx arrows, plus the
repository's Markdown) under five option sets (plain,
--pdfua,--pdfa,--compress,--page-numbers --toc) passqpdf --check11.3, and Poppler 22.12 extracts the same text and renders the same pixels at 40 dpi from them as from the hex strings. - With the code-block change alone, 8,600 generated documents of code blocks (tabs, CR and CRLF, byte-order marks, non-ASCII and invalid UTF-8, long and empty lines; both renderers, highlighting on and off: 17,200 PDFs) give the same bytes as before. Against 4.2.0, the corpus' 108 PDFs (12 documents, three styles, three flag sets) differ only where they have Unicode text: 72 identical, 36 Unicode, none other.
- Core tests, also under ASan and UBSan; a new check pins the bytes of a string with every escape.
- Each byte tested with four compares: +4.5 % instructions on the Unicode watch documents. Two table loads with a byte-wise store: still +0.6 %.
-
One byte a glyph for Latin
--pdfatext (a TrueType font embedded as a simple WinAnsi font): a build that wrote one byte a glyph was 0.6 to 1.3 % faster and 9 to 11 % smaller under--pdfa. The time--pdfacosts an ASCII document (+59 % onbaseline.md) is the Unicode renderer's wrapping and measuring, not its string bytes.
| Asset | 4.2.2 | 4.3.0 |
|---|---|---|
rayomd-<version>-linux-x64.tar.gz |
645,275 B | 645,597 B |
rayomd-<version>-linux-x64-static.tar.gz |
1,041,770 B | 1,042,000 B |
rayomd-<version>-linux-x64-curl.tar.gz |
647,384 B | 647,815 B |
rayomd-<version>-windows-x64.zip (GUI and CLI) |
1,482,699 B | 1,483,648 B |
The default edition passed the CLI verification on Debian 12 and writes the same PDFs as the build measured above; the static edition runs on Alpine.
4.3.1 changes how the TrueType font is loaded and subset, not what it writes: every PDF is the
same as with 4.3.0. The glyph table lives in memory that starts zeroed (a load cleared 128 KB);
hmtx and loca are read with one bounds test per table; the subset is planned and then written
once into its stream object, its kept tables read in place, its glyph list made once; its
checksum takes one pass of word reads. One-shot export against 4.3.0 (one pinned core, median of
41):
| Document | 4.3.0 | 4.3.1 |
|---|---|---|
| short note in Polish | 1.40 ms | 1.28 ms (-8.4 %) |
tester.md without images |
1.89 ms | 1.75 ms (-7.1 %) |
| 35 KB of C++ in code blocks, Polish comments | 2.07 ms | 1.91 ms (-7.8 %) |
README.md |
2.39 ms | 2.27 ms (-5.1 %) |
docs/development/performance.md |
4.86 ms | 4.69 ms (-3.6 %) |
| short note in ASCII (standard fonts) | 0.96 ms | 0.95 ms |
For the Polish note: 1.14 → 0.66 million user instructions and 187 → 156 page faults. Most of
what remains of a fresh export is the kernel's: starting the process, faulting pages in, and
loading libc, which the static edition skips. 2,100 mutated and truncated copies of DejaVu Sans
as RAYOMD_FONT give the same PDF, or the same failure, as 4.3.0, with no ASan or UBSan report.