The library, on your own machine
The engine that writes the documents goes inside your own software, in Rust or in Python, and draws its pages on your machine. This page says what that means, three times over: in everyday words, then for a developer, then with the figures.
In plain words
Written for anybody. You can stop at the end of it: it says the whole of what the library is and what it changes.
What this is about
You know the PDF file: it is the one that keeps exactly the same look everywhere. You open it on your computer, your neighbour opens it on a telephone, the print shop runs it on its machine, and it is the same page down to the millimetre. That is why invoices, contracts and payslips travel in that form.
HQF Development makes the engine that writes those files. Not a program you open and click around in: an engine, which the person who writes your software puts inside their own.
What a library is
In computing, a library is a set of ready-made parts. Think of a car engine. A manufacturer does not build one from scratch for every model: it takes an engine that exists, drops it into the body it drew, and connects the pedal to it. The engine turns the wheels; the car stays theirs.
A library is that engine. Whoever writes your invoicing software keeps their software, their screens, their habits, their way of working. They slip our engine inside, they tell it here are the invoice lines, here is the logo, here is the total, and the engine hands back the PDF file.
The other way round, and what changes
There is another way: send the information to a computer somewhere on the internet, which builds the document and sends it back. It is like taking your letter to a print shop across town. It works very well, and the team offers that too. But you have to carry the envelope over there, wait, and come back for it.
With the library, the print shop is in the next room. Three things change, and all three are felt straight away.
- It is immediate. The document is built on your own machine, in the same breath as the click that asked for it. Nothing leaves, nothing comes back. A hundred-page invoice comes out in a fraction of a second.
- Your information stays with you. Your customers' names, their addresses, their amounts: all of it stays on your machine from start to finish. The library works entirely on the spot, with what it carries in its own package. It is the simplest possible answer to the question of where your data goes: it goes nowhere.
- It stands on its own. Your software builds its documents even when the connection is down, even on an isolated site, even at three in the morning. There is nothing to keep an eye on but your own machine.
What it makes
Invoices, order confirmations, delivery notes, payslips, contracts, statements, sheets of labels, catalogues, certificates, boarding passes, whole books. With tables that carry on from one page to the next, logos, photographs, the little squares a telephone reads to pay, boxes people fill in on screen, and a password locking the lot when you ask for one.
Who this page is for
The rest of this page is written for the person who develops your software. If that is not you, the best thing to do is to hand them the address of this page: further down, they will find everything they need to judge it in ten minutes.
For a developer
Written for a developer: the bindings, the surface, the licensing model and what the build machine has to have. No detours.
Two bindings
The engine is written in Rust and is used from Rust directly, as an ordinary dependency of your project.
The Python side is a compiled extension shipped already built: nothing to compile on the target, no system library to install, no PDF toolchain on the box. It is built against CPython's stable ABI, so one Python wheel serves every interpreter version on a platform. CPython 3.9 and later, on Linux, Windows and macOS.
A C façade for the languages that call that way is planned for a future version.
What it writes
- Text: TrueType, OpenType and Type 1 parsed and embedded, subset to the glyphs actually drawn; ligatures, small capitals, old-style figures, kerning; a fallback font behind another; the fourteen faces every reader already carries; Type 3 faces whose glyphs are drawings; vertical writing.
- Layout: paragraphs broken to a width, columns, lists, tab stops and leader dots, styled runs inside a paragraph, tables paginated over as many pages as they need, blocks stacked without a single ordinate written by hand.
- Drawing: paths, curves, arcs, rounded corners, clipping, axial and radial gradients, tiling patterns, transparency and blend modes, soft masks, form drawings placed as often as wanted for the weight of one.
- Images: JPEG, PNG and raw samples; grey, RGB, CMYK, palette; alpha, stencil masks, clipping paths, ICC profiles, orientation.
- Colour: RGB, CMYK, grey, calibrated spaces, L*a*b*, ICC profiles, spot colour, several named colorants at once, indexed tables.
- Codes: Code 128, EAN-13, EAN-8, UPC-A, QR code, Data Matrix, PDF417, Aztec.
- Interactive parts: text fields, check boxes, drop-downs, radio groups, push buttons, signature fields, links, bookmarks, page labels, article threads, layers, page transitions.
- Standards: PDF/A for archiving, PDF/X for press, PDF/UA for accessibility, Factur-X for electronic invoicing, XMP metadata with schemas of your own, full page tagging.
- Protection: AES-256 encryption, user and owner password, permissions per action.
- Reading: open a PDF written by another tool and read its pages, metadata, links, form fields, bookmarks, layers and structure; take a whole page of it into the document you are writing.
- Weight and first paint: compression level, object streams, linearised output.
Getting it, and putting it in a build
Both artefacts come with the licence: the Python wheel for the Python side and the Rust crate for the Rust side, each pinned to a version. The team hands over the link at registration, together with the key, so a build machine pulls them the way it pulls any other pinned dependency.
The wheel drops into a virtual environment as it stands, and the crate is declared in your manifest at the version you were given. Nothing on the machine has to be built, and nothing has to reach the internet at run time.
What it asks of the machine that runs it
It works on the spot. Everything it needs is in the package: the fourteen standard font metrics, the colour profiles, the code tables. An integration check inspects the compiled artefact on every push to confirm the engine reaches nothing over the network. Your data stays on the machine that runs it.
It opens on a signed key, handed over by the team on purchase. The key is read from a file, an environment variable or a secrets manager, and given to the document in one call. Before a key is given, the engine writes an evaluation copy, marked on every page it draws: the document is complete and readable, and the mark says what it is. That is how to try it before buying it.
How a document is assembled
One shape, always: create a document, add resources to it — a font, an image, a drawing, a gradient — each of which hands back a handle, fill a content stream with drawing operators, make a page of it, add the page to the document, ask for the bytes.
Three layout engines sit above that groundwork and spare you every coordinate: the text flow breaks a paragraph to a width, the table measures its rows from what they carry and paginates itself, the stack places blocks one under the other. Alongside them: colour, images, forms, navigation, metadata, conformance, protection, and reading an existing PDF.
The detail, with its figures
Written for whoever is about to judge the surface: the counts, the families, the same code on both sides, and how a refusal comes back.
The public surface at a glance
Version 1.107.0 exposes 42 public modules and 177 names at the root of the Rust crate; the Python side declares 237 classes. 94 complete examples ship with it, each in two twin copies, one in Rust and one in Python, and each replayed on every push so that none of them ages quietly.
| Family | What is in it |
|---|---|
| Document and page | Document, Page, Content, Version, Compression, PageGroup, PageTurn, TabOrder |
| Fonts and text | Font, FontHandle, StandardFont, Type1Font, Type3Font, MissingGlyph, Run, WritingMode |
| Layout | TextFlow, Table, Row, Cell, Columns, ColumnWidth, RichText, Style, Stamp, Rect, Shadow, and in layout: Stack, Block, Placement, Rule, Stroke, Border, Padding, Margin, VerticalFlow, TaggedTable |
| Colour | Color, Rgb, Cmyk, Paint, CalGray, CalRgb, Lab, IccBased, Indexed, Separation, DeviceN, OutputIntent |
| Images and fitting | Image, ImageHandle, ImageSpace, Resolution, Orientation, ClippingPath, FitBox, FitMode, Anchor, Turn, Mirror |
| Gradients, patterns, functions | Axial, Radial, TilingPattern, Spacing, Function, Calculation, Op |
| Graphics state | ExtGState, BlendMode, SoftMask, OverprintMode, BlackPointCompensation |
| Drawings and layers | Drawing, Layer, LayerConfiguration, LayerRule, LayerPolicy |
| Codes | Code128, Ean13, Ean8, UpcA, QrCode, DataMatrix, Pdf417, Aztec |
| Forms and signing | FormField, CheckBox, ChoiceField, RadioGroup, PushButton, SignatureField, FieldBorder, Signature, Signer, IncrementalUpdate |
| Navigation | Bookmark, Link, Article, PageLabel, OpenAction, PageFit, ViewerPreferences, Transition, and the action module |
| Annotations | the annotation module: square, circle, line, polygon, polyline |
| Accessibility | StructureTree, StructElement, Marks, Scope, Artifact |
| Metadata and standards | the metadata module: Metadata, Xmp, XmpSchema, PdfA, PdfX, PdfUa, Invoice, Attachment; and Standard, Report, Finding |
| Protection | Encryption, Permissions |
| Reading | the read module: Reader, ImportedPage, Information, FormEntry, Outline, LayerEntry, Structure, Parts |
| Licence and reporting | License, Error, Warning, ResourceKind |
| Low level | cos, filter, incremental, platform |
The shortest useful document
In Rust
use hqf_pdf::content::Content;
use hqf_pdf::cos::Name;
use hqf_pdf::{Document, License, Page};
let mut c = Content::new();
c.set_fill_rgb(0.20, 0.40, 0.80)?;
c.rect(50.0, 700.0, 495.0, 90.0)?;
c.fill();
let mut page = Page::a4();
page.content = c.into_bytes();
let mut doc = Document::new();
doc.set_license(License::from_key(key)?);
doc.set_info(Name::new("Title"), "hqf-pdf sample invoice");
doc.add_page(page)?;
let bytes = doc.to_bytes()?;
In Python
import hqf_pdf
content = hqf_pdf.Content()
content.set_fill(hqf_pdf.Rgb(0.20, 0.40, 0.80))
content.rect(50.0, 700.0, 495.0, 90.0)
content.fill()
page = hqf_pdf.Page.a4()
page.set_content(content)
document = hqf_pdf.Document()
document.set_license(hqf_pdf.License.from_key(key))
document.set_info("Title", "hqf-pdf sample invoice")
document.add_page(page)
data = document.to_bytes()
Text in an embedded font
Only the glyphs the document draws go into the file: a page holding one line carries one line's worth of font, not the whole typeface. Measuring goes through the same handle that draws, which is what keeps text inside its box.
In Rust
let font = Font::parse(fs::read(&font_path)?)?;
let handle = doc.add_font(font);
let mut c = Content::new();
c.begin_text();
c.set_font(handle.name(), 14.0)?;
c.text_position(60.0, 760.0)?;
for line in &lines {
c.show_glyphs(&handle.glyphs(line));
c.text_position(0.0, -28.0)?;
}
c.end_text();
let width = handle.measure(lines.last().map_or("", String::as_str), 14.0);
In Python
font = hqf_pdf.Font.from_path(font_path)
handle = document.add_font(font)
content = hqf_pdf.Content()
content.begin_text()
content.set_font(handle, 14.0)
content.text_position(60.0, 760.0)
for line in lines:
content.show_glyphs(handle.glyphs(line))
content.text_position(0.0, -28.0)
content.end_text()
width = handle.measure(lines[-1], 14.0)
A paragraph that breaks itself
In Rust
let flow = TextFlow::new(&handle, 11.0).align(align).leading(15.0);
let lines = flow.break_lines(words.paragraph, box_width);
flow.draw(&mut c, &lines, x, top, box_width)?;
In Python
flow = hqf_pdf.TextFlow(handle, 11.0, align=align, leading=15.0)
lines = flow.break_lines(words.paragraph, box_width)
flow.draw(content, lines, x, top, box_width)
A table that carries on page after page
A row height is never given: it is measured from the text the row carries, once the column widths are known. That is what lets the table decide, row by row, whether the next one still fits. Fitting draws nothing — it returns the geometry, so a table can be measured before anyone commits to where it goes — and one call does the whole round and returns one placement per page.
In Rust
let columns = Columns::new(
vec![
ColumnWidth::Fraction(1.0),
ColumnWidth::Points(46.0),
ColumnWidth::Points(94.0),
ColumnWidth::Points(94.0),
],
TABLE_WIDTH,
)?;
let mut table = Table::new(columns);
table.header(1);
table
.rule(Rule::Frame, Stroke::black(0.8))
.rule(Rule::HorizontalOther, Stroke::new(0.25, Rgb::gray(0.75)));
let pad = Padding::symmetric(5.0, 4.0);
table.push(
Row::new()
.cell(Cell::new(text, 9.0, label).padding(pad))
.cell(Cell::new(text, 9.0, value).padding(pad).align(Align::Right)),
);
let mut start = 0;
loop {
let placed = table.fit(MARGIN, TOP, TOP - BOTTOM, start)?;
let mut content = Content::new();
placed.draw(&mut content)?;
let mut page = Page::a4();
page.content = content.into_bytes();
doc.add_page(page)?;
match placed.outcome() {
FitProgress::Done => break,
FitProgress::BoxFull { next_row } => start = next_row,
FitProgress::RowTooTall { row } => return Err(too_tall(row)),
}
}
In Python
columns = hqf_pdf.Columns(
[
hqf_pdf.ColumnWidth.fraction(1.0),
hqf_pdf.ColumnWidth.points(46.0),
hqf_pdf.ColumnWidth.points(94.0),
hqf_pdf.ColumnWidth.points(94.0),
],
TABLE_WIDTH,
)
table = hqf_pdf.Table(columns)
table.header(1)
table.rule(hqf_pdf.Rule.frame(), hqf_pdf.Stroke(0.8))
grey = hqf_pdf.Rgb.gray(0.75)
table.rule(hqf_pdf.Rule.horizontal_other(), hqf_pdf.Stroke(0.25, grey))
pad = hqf_pdf.Padding.symmetric(5.0, 4.0)
table.push(
hqf_pdf.Row(
[
hqf_pdf.Cell(font, 9.0, label, padding=pad),
hqf_pdf.Cell(font, 9.0, value, padding=pad, align=hqf_pdf.Align.Right),
]
)
)
start = 0
while True:
placed = table.fit(MARGIN, TOP, TOP - BOTTOM, start)
content = hqf_pdf.Content()
placed.draw(content)
page = hqf_pdf.Page.a4()
page.set_content(content)
document.add_page(page)
if placed.done:
break
start = placed.next_row
How refusals come back
In Rust, everything that can refuse returns a result carrying the library's own error type: a non-exhaustive enum of 76 variants, each naming precisely what happened — a malformed font, a malformed image, no such font, no such page, two fields named alike, something missing for print, something missing for archiving, something the conformance level forbids, a row taller than its box, no licence, an invalid licence, an encrypted file. A caller matches on the exact case rather than on message text.
The library calls neither unwrap nor panic on a production path: data from outside — a PDF, a font, an image, a profile — that does not hold up is an ordinary outcome, returned as such.
In Python the same thing raises one error class. Two layout cases have an exception of their own, both subclasses of the built-in value error, so a pagination loop can catch them apart: a table row taller than the box it was given, and a stack block in the same position.
Beside refusals, remarks. Writing the bytes has a reporting form that hands back the bytes and the list of warnings the document has about itself; the file is written and valid either way, and a warning points at a document that is probably not the one the caller meant. Checking against a standard goes further: it returns everything known to stand between the document and that standard, writing nothing and changing nothing.
What the current version covers
Version 1.107.0: writing and reading PDF, all three generations of protection on the reading side and the modern one on the writing side, PDF/A-1 through PDF/A-4, PDF/X-4, PDF/UA-1 and PDF/UA-2, Factur-X, full tagging, PAdES signing with the caller supplying the signer, linearised output, object streams, and taking pages from other files.
Planned for a future version: a C façade for the languages that call that way, and the JBIG2 and JPEG 2000 image formats.
The manual
The engine's full manual — every call, every setting, every pitfall — is handed over in direct conversation with the team, and that is on purpose.
The reason is simple: what really helps is the part that answers your question, with the example that matches your case. So the route is this. You register on this site, you write to us about what you are trying to do — an invoice carrying its XML, a table of two thousand rows, a sheet going to press — and the team sends you back the part of the manual that covers exactly that, with the code on both sides, Rust and Python.
You are talking to the people who wrote the engine, and the answer comes cut to fit your project. In the meantime, the 94 examples shipped with the library cover most of what it can be asked for, and each one runs as it stands.
Where to go next
Everything it puts on a page, group by group The other way: ask the server for the page Register, and tell us what you are building Ask us anything this page left out
Glossary
- PDF library
- A piece of software your own programs use to write PDF documents. It has no window and no buttons: your program tells it what to draw, and it hands the finished file back. This site also calls it the engine, which is the same thing under another name. The server is a second program that holds the engine and answers whoever asks it for a document over the network, and the rendering service is that server running on our machines rather than on yours.
- byte
- The unit a file's weight is counted in, the way a parcel is counted in grams. A thousand of them make a kilobyte, and a million make a megabyte — the size of one photograph taken with a telephone. A one-page PDF here weighs between six thousand and a hundred and twenty thousand, so a hundred of them fit in the space of five photographs.
- standard
- A rule argued out in committee, published under a number anybody may buy and read, and identical for every firm claiming it. A claim to follow one can therefore be checked against the text. A way of working that merely spread because it worked is a habit of the trade: useful, widespread, and answerable to no text at all.
- PDF/A
- A PDF made to be readable in twenty years' time. Everything it needs is inside the file — the fonts, the colours, the description of what it is — so nothing it depends on can go missing later. Public bodies and archives ask for it by name.
- PDF/UA
- A PDF a blind reader can use. The file itself says which text is a heading, which is a paragraph, and in what order the software that reads a page aloud should say it all. Without that, the software reads the page in the order it was drawn, which is rarely the right one.
- Factur-X
- An invoice that is a page for a person and a data file for a machine, in one document. The page looks like any invoice; inside, the same amounts are attached in a form accounting software reads without anybody retyping them. French law requires this exchange between companies.
- the identity card of a file
- The block inside a file that says what the file is: its title, who made it, when, and which rules it follows. Search engines and archives read it; a reader never sees it. Its technical name is XMP.
- colour profile
- A file that says what a colour actually looks like. Without one, the same red is one red on your screen and another on a press. A document meant for print carries the profile its colours were chosen against.
- PDF/X
- A PDF a printing works can put straight on the press without asking anybody anything: the colours are stated in a way the press understands, the lettering is inside the file, and the page says where the blade cuts and how far the ink runs past it.
- CMYK
- The four inks a printing press lays down: cyan, magenta, yellow and black. A screen mixes light instead, so a document going to a press states its colours in inks, not in screen colours.
- RGB
- The three lights a screen mixes a colour from: red, green and blue. All three at full is white, all three off is black. It is how a colour is said for anything lit from behind, and the wrong way to say one for a press, which lays ink on white paper instead.
- spot colour
- An ink the printing works keeps ready mixed and prints from a plate of its own, instead of building the colour out of the four usual ones. A firm's own red comes out that exact red, on every run and on every paper, and a two-ink job costs less than a four-ink one.
- font
- The drawing of every letter, digit and mark a document writes, held in a file of its own. A PDF carries the fonts it is set in inside itself, which is why it opens looking the same on a machine that has never had them. Leave them out and a reader puts another font in their place, and the layout moves.
- lightened typeface
- Putting into the file only the letters the document actually draws, instead of the whole typeface. A typeface holds thousands of shapes; a letter or an invoice uses a few dozen. Carrying only those is what makes the files here small.
- glyph
- One drawn shape of a typeface. The letter A is a character; the particular drawing of it that lands on the page is a glyph. The same letter is drawn differently in every typeface.
- gradient
- A colour that changes across the space it fills, with no step anywhere between one end and the other: a header that fades out, a bar with some depth to it. It is described once, in a handful of numbers, and painted wherever it is wanted, so it stays sharp at any size and weighs almost nothing.
- transparency
- Paint that lets what is underneath it come back through, by a stated amount. It is what makes a mark laid across a page leave the text under it readable, and a highlight a highlight rather than a blot. The file says how much comes through; whatever draws the page works out the colour that results.
- QR code
- The square of black and white dots you point a phone at: on a poster, on a restaurant table, at the foot of a bill. It carries a web address, or the exact details of a payment, so nobody has to type anything in. On an invoice it turns paying into one tap.
- DataMatrix
- A tiny square code, sometimes smaller than a grain of rice, for things with no room for anything bigger: a box of medicine, an electronic part, a parcel. It is still read when a corner of it is scratched or smudged, which is why factories and pharmacies print it.
- Aztec
- The code with a little target in the middle, the one on a train ticket or on a boarding pass held up on a phone screen. It was made to be read off a lit screen and through a scratched window, and it takes less room than the others for the same text.
- PDF417
- A wide, flat code made of stacked bars, the one on the back of a driving licence, on an airline boarding pass or on a shipping label. It holds several hundred characters — a whole identity, a whole delivery line — where an ordinary striped code holds a number.
- signature
- A seal inside the document that proves two things at once: who approved it, and that not one byte has changed since. A reader shows it as a banner at the top of the page.
- bookmark
- The list of headings a PDF reader shows down the side of a document, each line leading straight to its place in it. It is the table of contents the software owns rather than the one printed on a page, and a long document without it is one nobody can find their way around.
- binding
- The bridge that lets two pieces of software written differently talk to each other. The engine is written in Rust; the Python binding is what lets a Python program call it as if it had been written in Python.
- layer
- A named group of things drawn on a page that a reader can switch on and off, like tracing paper laid over a plan. A drawing can hold its measurements on one and its notes on another, and a layer can be set to show on screen and stay off the printer.
- form field
- A box on a PDF page somebody can fill in, tick or choose from, and whose answer the file keeps. It has a name, so whoever collects the file reads the answers back by name rather than by looking at the page.
- Python wheel
- The file a piece of Python software is delivered in, ready to use. It holds the software already built for one family of machines, so putting it in place is a copy rather than a build: nothing is compiled on the machine that receives it, and nothing else has to be installed beforehand.
- Rust crate
- The unit a piece of Rust software is delivered and named in. A project lists the crates it wants and the version of each, and the build tool fetches them and compiles the lot together into one program.
- TrueType
- One of the two shapes a typeface file comes in, and the commonest. It holds the drawing of every letter and enough instructions to keep it readable at small sizes. A document carries the file inside itself.
- OpenType
- The wider shape a typeface file comes in, holding either kind of letter drawing plus the typographic niceties: the pairs of letters that fit together, the figures that line up in a column, the small capitals.
- Type 1 font
- The older shape a typeface file comes in, still used where a particular cut of a typeface only ever existed that way. A document carries the file as it was supplied.
- page label
- The number a reader shows for a page, which is not the same thing as its position in the file. It is what lets a foreword run i, ii, iii and the body start again at 1.
- blend mode
- The rule for what happens to the colour where one mark is laid over another: covered up, darkened, lightened, multiplied. The plain rule is that the top one wins.
- kerning
- Closing up the gap between two particular letters that would otherwise sit too far apart, such as a capital A after a capital T. The typeface itself carries the list of pairs.
- ligature
- Two letters drawn as one shape because they collide when set separately, as an f followed by an i does. The typeface carries the joined drawing.