Part 3 of 3

Building a PDF Creation Library in MQL5 (Part 3): The Byte-Exact Writer

Building a PDF Creation Library in MQL5 (Part 3): The Byte-Exact Writer

Why this article was written

Part 2 showed what goes wrong when a human maintains byte offsets by hand: edit one string and three numbers rot at once, and the format will not tell you.

Part 3 removes the human from that job entirely. The whole article rests on a single rule:

Never calculate a byte offset. Ask the buffer where it is.

Every number that describes the file is derived from the file, at the moment it is written. There is nothing left to keep in sync.

What is inside

CPdfBuffer — bytes, and the current position. A growable byte array that knows its own length. PutByte, PutText, PutLine, PutInt, PutReal, PutPadded, PutBuffer, SaveToFile.

  • Growth is doubling, not incremental. The article explains why that turns the cost of building a document from quadratic into linear — about 18 reallocations for its example rather than thousands.
  • PutReal has a job you would not expect: stopping MQL5 from writing exponent notation. A value below half the last printed digit is clamped to zero, trailing zeros are trimmed, and -0 becomes 0. A PDF with 1e-07 in it is not a valid PDF.
  • SaveToFile writes m_length bytes, not the array. Writing the whole array would append a tail of zero bytes after %%EOF.

CPdfWriter — objects, and where they landed. It hands out object numbers, records where each one lands, and writes the cross-reference table from what it observed.

  • The line the class exists for is BeginObject: record m_offset[id] = m_out.Length(), then write the header. One line, and the entire category of bug from Part 2 is gone.
  • Numbers before objects. NewObjectId() reserves an id you can reference before the object exists — which is how a page can point at a font that has not been written yet.
  • Three guards on BeginObject: an id that was never allocated, an object written twice, an object opened while another is still open.
  • Failing once. The first error is recorded and printed; later ones are ignored, so the message you see is the one that actually matters.
  • Finish refuses to produce a broken file. It checks that nothing is still open, that the root exists, and that every allocated id was actually written. Save refuses unless Finish succeeded.
  • A binary marker. Begin() writes four high bytes in a comment after the header, so transfer tools treat the file as binary rather than text.

The demo that proves it. Demo03_HelloWriter.mq5 rebuilds Part 1's file through the new API — 599 bytes, 5 objects, validator reports no problems. Every offset shifted by +6 or +7 because of the binary marker, and not one of them had to be touched by hand. That is the whole argument, measured.

And one that is actually useful. Demo03_TextToPdf.mq5 reads a text file and paginates it into a PDF: Courier so character width is predictable, pages reserved in advance, TL/T* for leading, a page N of M footer, and EscapePdf for backslashes and parentheses — backslash first, and the article explains why that order is not optional.

An honest note on scope

PDF syntax is still assembled as text. Dictionary keys and resource names are the caller's business. String escaping covers only parentheses and the backslash — control characters and bytes above 126 need an encoding decision this part has not made, so Part 5 replaces that function. Proportional font widths are not measured; Part 6 is entirely about that.

And the honest headline: the library still cannot draw. Part 4 starts on it.

MQL5
PdfValidate.mqh   Include/MQLPDF/   Part 2, carried over unchanged
PdfDefines.mqh    Include/MQLPDF/   shared constants                   <- today
PdfBuffer.mqh     Include/MQLPDF/   bytes, and the current offset      <- today
PdfWriter.mqh     Include/MQLPDF/   objects, xref table, trailer       <- today

More in this series

Generating real PDF documents from MetaTrader in pure MQL5 — statements, trade tickets and performance reports — with no DLL and no external library. About this series

  1. Part 1Writing a PDF by HandSeptember 24, 2026
  2. Part 2Why PDFs BreakOctober 19, 2026
  3. Part 3The Byte-Exact Writer — you are hereOctober 29, 2026

Continue reading on MQL5.com

The full article with complete source code is published on MQL5.com.

Read the full article