PDFGen writes PDFs from a single C file with no dependencies
Simple C PDF Writer/Generation library
At a glance
- What is it?
- PDFGen is a public domain C library that creates PDF documents from one .c file and one header, covering text, vector shapes, barcodes, images and outlines, with RC4 40-bit encryption and no support for PNG alpha channels. Its build system is heavier than the library itself.
- Who is it for?
- PDFGen suits a C program that needs to emit a small PDF as part of its own output and refuses to take on a third party dependency, a report exporter or a labelling tool. It does not suit a product that needs strong encryption, since the only protection offered is RC4 at 40 bits, and it does not suit a team that wants PNG transparency, which the image support does not handle.
- Can I use it commercially?
- Yes. Unlicense is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 44 days ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One source file, one header, no external libraries
PDFGen is a C library for creating PDF documents, described as simple, with everything contained in a single C file with a header and no external library dependencies. The repository layout matches that claim, with pdfgen.c and pdfgen.h sitting at the top level next to the Makefile, the tests directory and a data directory holding test assets. The stated purpose is narrow and worth repeating accurately, since it defines where the library fits: it is useful for embedding into other programs that require rudimentary PDF output. Rudimentary is the author's word, and the feature list that follows explains why. There is no object model to learn and no document tree to keep in sync, which is exactly the appeal when you are emitting a receipt or a label from an existing C program and do not want a dependency tree for it.
Text and vector drawing cover the everyday document
The supported feature set starts with text of various fonts, sizes, colours and rotation, including TrueType fonts. On top of that sits a set of primitive drawing elements: lines, rectangles, filled rectangles, polygons, filled polygons, Bezier curves, circles and ellipses, and custom paths. Bookmarks and links are supported, and so are barcodes, in several symbologies: Code-128A, Code-39, EAN-13, UPC-A, EAN-8, UPC-E and QR codes. Images can be embedded from PPM, PGM and PBM in both binary and ASCII forms, from JPEG, from PNG and from BMP. Two limits are stated plainly in the list and both are worth knowing before you commit. PNG alpha channels are not supported, so transparency in an embedded PNG is not available, and the encryption support is described as password protected output using RC4 at 40 bits, which is a weak scheme by any modern standard and would only ever be useful for casual obfuscation.
The API reads like a drawing sequence
The usage example in the documentation is short enough to read in full and shows the shape of the whole API. You fill a struct pdf_info with document metadata, create a document with pdf_create passing a page width and height, set a font, append a page, then issue drawing calls before saving and destroying:
#include "pdfgen.h"
int main(void) {
struct pdf_info info = {
.creator = "My software",
.producer = "My software",
.title = "My document",
.author = "My name",
.subject = "My subject",
.date = "Today"
};
struct pdf_doc *pdf = pdf_create(PDF_A4_WIDTH, PDF_A4_HEIGHT, &info);
pdf_set_font(pdf, "Times-Roman");
pdf_append_page(pdf);
pdf_add_text(pdf, NULL, "This is text", 12, 50, 20, PDF_BLACK);
pdf_add_line(pdf, NULL, 50, 24, 150, 24, 3, PDF_BLACK);
pdf_save(pdf, "output.pdf");
pdf_destroy(pdf);
return 0;
}The calls are positional, with a page pointer that is NULL for the current page, a font size, x and y coordinates and a colour constant. There is no layout engine underneath, so text does not wrap and does not reflow. Every position is yours to compute.
The Makefile is a static analysis harness, not a build convenience
The README explains that this code base is used to test static analysis tools, and that the build system is consequently more complex than a project of this size would need. The Makefile shows why. Compilation uses a long list of warning flags including -Wall, -pedantic, -Wsuggest-attribute=const, -Wmissing-field-initializers and -Wold-style-declaration, plus coverage instrumentation, and separate object and executable suffixes are switched for Windows. The default target builds both the test program and a tests/massive-file binary, and there are dedicated targets for fuzzing built with clang under the address, undefined and integer sanitizers. The check target runs cppcheck over the sources, compiles with -Werror, runs the shell test script, and pipes the files through clang-format and colordiff to catch formatting drift. The pinned clang-format version in the container is explained in a comment, since the Ubuntu package formats compound literals differently from newer versions.
A container image with the whole analysis stack
The Dockerfile shows how heavy the tooling around this small library has become, and it is worth reading before you set up an environment. The base is ubuntu:24.04, and the package list includes clang and clang-tools, gcc, g++, cppcheck, doxygen, graphviz, ghostscript, poppler-utils, pdftk-java, valgrind, gcovr, mingw-w64 and zbar-tools, along with python3 for scripting and a pinned clang-format installed through pip with the break-system-packages flag. On top of that the image downloads and unpacks the Facebook Infer static analysis release into /opt and adds its bin directory to PATH. The result is a reproducible environment for building, testing, fuzzing and analysing a library that is itself one C file. The trade is clear: reproducibility and coverage come at the price of a large image and a toolchain download before you can compile anything.
Vendoring it in, or calling out to a PDF tool
The alternative to embedding PDFGen is not another library but a different shape of solution: run an external program that produces the PDF and hand it a description of what you want. The difference in approach is where the output logic lives. With an external tool you keep the generation rules in one process and the renderer in another, which means a runtime dependency on that binary, a way to pass your data into it, and a failure mode where the tool is missing or the format it accepts is not quite the one you need. With an embedded writer like this one, the document code sits in your process, there is nothing to install at runtime, and the feature set is exactly what the library implements, which is also its limit. Another route exists for non C callers, since the repository lists a Ring language binding, RingPDFGen, so a project in that language can reach the same code without a C shim of its own.
Unlicense terms, an old tag and a recent commit
PDFGen is released under the Unlicense, and the source is described as public domain, with the author asking for a line at his address if the code proves useful. That is the most permissive licensing available, so vendoring the file into a proprietary product raises no licence question, though it is worth reading the LICENSE file rather than relying on a badge. On the versioning side the picture is mixed: the only listed release is 0.1.0 from 2021-06-07, marked as a release for use in clib, so the tag tells you almost nothing about the current state of the tree. The last push was on 2026-09-11, so the code is still being touched. The package.json in the repository carries the same 0.1.0 version and lists pdfgen.c and pdfgen.h as the source files, which is consistent with a library distributed primarily as copied source.
Editorial conclusion
PDFGen suits a C program that needs to emit a small PDF as part of its own output and refuses to take on a third party dependency, a report exporter or a labelling tool. It does not suit a product that needs strong encryption, since the only protection offered is RC4 at 40 bits, and it does not suit a team that wants PNG transparency, which the image support does not handle. Verify first that the features you need exist in the single file you will vendor, that your toolchain can compile it under the strict warning flags in the Makefile, and that the Unlicense terms match how you intend to redistribute the code.
Frequently asked questions
What does PDFGen need to build?
The library itself is one C file with a header and no external library dependencies, so a C compiler is enough. The repository Makefile is heavier because it exists to exercise static analysis tools, and the Dockerfile installs clang, cppcheck, valgrind, Infer and other analysis tools along with the build essentials.
Which PDF features does PDFGen support?
Text with fonts, sizes, colours and rotation including TrueType fonts, vector primitives such as lines, rectangles, polygons, Bezier curves, circles and custom paths, bookmarks and links, barcodes including Code-128A, Code-39, EAN and UPC symbologies and QR codes, and embedded PPM, PGM, PBM, JPEG, PNG and BMP images.
Can PDFGen encrypt or protect a PDF?
It supports password protected output using RC4 at 40-bit encryption. PNG alpha channels are not supported for embedded images, so transparency is not available for that format.
What licence is PDFGen under?
It is published under the Unlicense, and the source is described as public domain, so the code can be vendored into other programs. The LICENSE file in the repository holds the authoritative terms.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/andrerenaud-pdfgen)Was this useful?