Getting started
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 "text=@report.txt" \
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.
How the layout works
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:
- Line spacing (
vertSpace) is the distance between one row and the
next, in points. 12 points gives six lines per inch, the line-printer standard.
- Point size (
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.
1. Form feeds
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.
2. A fixed number of lines
Some files have no form feeds but a consistent page depth, often 66 or 72 lines. Set
lines to that number.
3. A marker line
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.
Skip lines, or skip each page?
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.
Every option
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.
Common problems
My box-drawing characters are garbage
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.
Every other page is blank
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.
A control line is showing at the top of every page
Set skipEachPage to 1. For a line at the very start of the file only, use
skipLines instead.
Everything sits slightly too low or too far right
Adjust topmargin and leftmargin. These shift the whole block and will not
disturb the spacing between rows.
The columns drift further out of line further down the page
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.
My file has no form feeds and pages of different lengths
Use splitOn, described in Where pages break.
Strange characters appear where control codes were
Leave Strip control codes ticked. It removes embedded printer escape sequences that
would otherwise render as visible junk.
Calling the API
There are two endpoints. Both do the same rendering and differ only in how you hand over the data.
Multipart, returns a PDF directly
The simplest to use from a shell script or a legacy box with curl on it.
curl -F "text=@report.txt" \
-F "background=@letterhead.pdf" \
-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.
JSON, returns base64
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.
What comes back when something is wrong
| 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. |
Limits and API keys
The service is free to use with no sign-up, limited per IP address:
- 60 requests per minute
- 150 requests per day
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 "text=@report.txt" \
-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.