Plenty of older business systems still produce plain text. ERPs, accounting and order-processing packages, stock and warehouse systems, anything that prints fixed-width reports or spools to a line printer. The invoices, statements and delivery notes they generate come out as flat text with no styling.
This tool turns that text into a tidy, branded PDF. Really though, it's here to help you find the right settings. Load a sample of your output, adjust the options until the preview looks right, then take those settings away and use them in your own setup. The rendering itself runs on the API. This page is just where you work out the configuration.
Lines/page (how long a page is in your source), Skip each page (removes a
header line that repeats at the top of every page) and Encoding (use cp850
for old box-drawing characters).Every option is explained in the Documentation tab, and hovering any label on the left shows what it does. Your files aren't stored. They're only sent across to build the preview.
This service takes the plain, fixed-width text your system already prints and renders it as a PDF, optionally on your own letterhead. It changes nothing about the application producing the text. You keep printing to a file or a spool queue exactly as you do now, and the PDF is built from that.
There are two parts. The page you are on is a settings tool: load a sample of your output, adjust until the preview looks right, then take those settings away. The API is the part you actually integrate with, and it is where the rendering happens.
The quickest possible start, using the free tier with no sign-up:
curl -F "[email protected]" \
https://pdf.tachytelic.net/api/render-raw -o report.pdf
That gives you a plain PDF with default settings. Everything below is about making it match your stationery exactly.
Fixed-width output is a grid. Every character occupies the same width and every line sits the same distance below the last, which is exactly how a line printer behaved. The renderer reproduces that grid rather than trying to reflow anything, so columns stay lined up.
Two numbers control it:
vertSpace) is the distance between one row and the
next, in points. 12 points gives six lines per inch, the line-printer standard.pointSize) is how big the characters are. It does not
affect row positions, only how the text looks within them.A row's position is simply topmargin + (row number × vertSpace), measured from the top
of the page. So if you are matching pre-printed stationery, work out those two numbers first and the
rest follows. Nudging topmargin and leftmargin shifts the whole block
without disturbing the alignment between rows.
Matching an existing form? Set vertSpace before anything else. If the
row spacing is wrong, no amount of margin adjustment will make the rest line up, because the error
grows with every line down the page.
All measurements are in points, where 72 points is one inch. A4 is 595 by 842 points, LETTER is 612 by 792.
This is the part that most often needs thought. Legacy files divide into pages in one of three ways, and the renderer supports all three. They can be combined, and whichever break comes first wins.
Most spool files contain a form feed character (0x0C) at each page boundary. This is
handled automatically, and formFeeds is on by default. If your file has them, you
usually need to do nothing else.
Some files have no form feeds but a consistent page depth, often 66 or 72 lines. Set
lines to that number.
The awkward case: no form feeds, and pages that vary in length. What these files do have is a header
line that repeats at the top of every page. Put some of that line in splitOn and a new
page starts wherever it appears.
The marker line becomes the first line of the new page, because that is what it is.
To hide it, set skipEachPage to 1, which blanks it while leaving every
other row exactly where it was. If the header carries something you want printed, such as an order
number or a date, leave skipEachPage alone and it stays visible.
splitOn is a plain piece of text by default, so a marker like INVOICE (CONT.)
needs no special treatment. Tick Split on is a regex when you need to be more
precise. For example, ^pic\s+\d+ matches a header line whatever order number follows it,
where the literal text would only ever match one document.
The one that catches people out: lines is a physical limit and always
applies, even when you are using splitOn. Leave it blank and the default for A4 is
about 64 rows, which is shorter than a typical 66 or 72 line form. The result is a blank filler
page between each real page.
If you use splitOn, set lines to your form's depth as well.
Two similarly named options that do different jobs:
skipLines blanks the first N lines of the whole document. Use it for
a one-off control line at the very top of the file.skipEachPage blanks the first N lines of every page. Use it for a
header that repeats.Both blank the lines in place rather than deleting them, so nothing below shifts up and your alignment survives.
All options are optional. The defaults suit ordinary fixed-width output. In the API, names are
PascalCase (PointSize); the tool shows the friendly label.
| Option | API field | Default | What it does |
|---|
The pages option accepts all, first, last, a
single number, a range such as 1-3, an open range such as 2- or
-3, or a list such as 1,3,5-7. Page numbers stay stable whichever subset
you ask for, so you can render the same file more than once with different letterheads and merge the
results afterwards.
Set Encoding to cp850, or cp437 on older US systems. Files
from DOS and Unix systems of that era are almost never UTF-8. You will also need to upload a
monospace font file, because the three built-in fonts cannot draw those characters.
Your lines value is smaller than the real page depth, so each source page overflows onto
a second one. Set lines to the actual form depth, commonly 66 or 72.
Set skipEachPage to 1. For a line at the very start of the file only, use
skipLines instead.
Adjust topmargin and leftmargin. These shift the whole block and will not
disturb the spacing between rows.
That is vertSpace, not the margins. A small error per row accumulates down the page.
Fractional values are allowed and often needed, for example 12.083.
Use splitOn, described in Where pages break.
Leave Strip control codes ticked. It removes embedded printer escape sequences that would otherwise render as visible junk.
There are two endpoints. Both do the same rendering and differ only in how you hand over the data.
The simplest to use from a shell script or a legacy box with curl on it.
curl -F "[email protected]" \
-F "[email protected]" \
-F 'options={"paper":"A4","lines":66,"skipEachPage":1}' \
https://pdf.tachytelic.net/api/render-raw -o report.pdf
Form fields: text (required), background, fontFile, and
options as a JSON string using the lower-case names from the table above. The response
body is the PDF itself.
Better suited to Power Automate, Logic Apps and similar tools, which prefer JSON both ways.
POST https://pdf.tachytelic.net/api/render
Content-Type: application/json
{
"TextFileContent": "<base64 of your text file>",
"BackgroundPdfContent": "<base64 of your letterhead PDF>",
"Paper": "A4",
"Lines": 66,
"SkipEachPage": 1
}
Options use PascalCase here and sit at the top level of the object rather than in a nested
options field. The response is {"PdfFileContent": "<base64 of the PDF>"}.
Use the Copy as curl box on the Tool tab to get this filled in with your current
settings.
| Status | Meaning |
|---|---|
200 | Success. The PDF is the response body, or base64 in PdfFileContent. |
400 | An option was rejected. The body explains which one, for example an invalid page spec or a malformed regular expression. |
429 | Rate limited. See below. |
500 | Something failed unexpectedly on our side. Worth reporting. |
There is a Power Platform connector, so you do not need to build the HTTP request yourself. Add the Render text to PDF action and fill in the fields. The options are the same ones described above, shown with their friendly names.
This is the one thing that catches people out, and it fails quietly rather than with an error.
TextFileContent, BackgroundPdfContent and FontFileContent are
declared as binary fields. Power Automate encodes them for you. If you wrap the value in a
base64() expression first, it gets encoded a second time, and what arrives is one
enormous single line of base64 rather than your document.
The result is a PDF containing your letterhead and no text at all, because that single line is then
blanked by Skip lines or Skip each page. Nothing errors, you just get an
empty page.
Feed the output of Get file content straight into the field instead. The service now
rejects double-encoded text with a clear message rather than returning a blank page, but the fix is
to remove the base64() call.
cp850 with box-drawing characters, where you need the original bytes preserved and
decoded with the Encoding option rather than guessed at.Supply one or the other. If both are present, TextFileContent wins.
The action returns PdfFileContent. Pass it straight to Create file or an email
attachment. Power Automate handles the decoding, so again, no base64ToBinary() is needed
in the ordinary case.
The service is free to use with no sign-up, limited per IP address:
That is plenty for evaluating it and for light day-to-day use. Go over it and you get a
429 with a short explanation.
If you are running something in production, an API key removes the shared limit and gives you your own daily allowance. Send it as a header:
curl -F "[email protected]" \
-H "x-api-key: your-key-here" \
https://pdf.tachytelic.net/api/render-raw -o report.pdf
Keys also mean your usage is not affected by anyone else sharing your outbound IP address, which matters if you are behind a corporate NAT or calling from a cloud service.
To get one, or to talk about anything unusual in your output, get in touch. If you are working with SCO OpenServer specifically, there is a full walkthrough for that platform.