Contributing guidelines

Contributions are welcome!

Big changes

Please open a new issue to discuss or propose a major change. Not only is it fun to discuss big ideas, but we might save each other’s time too. Perhaps some of the work you’re contemplating is already half-done in a development branch.

Code style: Python

We use ruff to format code and imports. The settings for this program are in pyproject.toml and setup.cfg. Pull requests should follow ruff code style.

Code style: C++

The file .clang-format contains our C++ format based on Clang’s formatter, imperfect as it is. We eagerly await a dangling parenthesis (https://reviews.llvm.org/D33029).

In general we prefer to make our C++ look similar to Python PEP8, within reason, because our code is primarily a Python binding. That is, variable and method names are snake_case, class names are CamelCase. Our coding conventions are closer to nanobind’s than qpdf’s. When a C++ object wraps a Python object, it should follow the Python naming conventions for that type of object, e.g. auto Decimal = py::module_::import_("decimal").attr("Decimal") for a reference to the Python Decimal class even though it is a C++ object.

We don’t like the traditional C++ .cpp/.h separation that results in a lot of repetition. Headers that are included by only one .cpp can contain a complete class, and get the -inl.h suffix, unless multiple inclusion is required. Where possible, combined the C++ interface and implementation in a single .cpp or -inl.h file. For classes that actually provide an interface used by multiple translation units, a full header is necessary.

Private and protected data members are named with a trailing underscore, as in count_. This tells a member from a local variable or a parameter at a glance, lets a constructor parameter have the natural name (: count_(count)), and makes this-> unnecessary, so we don’t write this->, whether for data members or for calls to member functions. Public data members of a struct-like class, which other code reads as obj.field, have no underscore. A commit hook rejects this->; when removing one, check that no parameter or local variable has the same name as the member, since the code would still compile and would quietly use the wrong one.

Use RAII. Avoid naked pointers. Use the STL, use std::string instead of char *. Use #pragma once as a header guard rather than silly #ifdef; they have been around for 25 years.

Adding C++ code

Where possible, we implement features in Python, unless qpdf provides them.

Tests

New features should come with tests that confirm their correctness.

New dependencies

If you are proposing a change that will require a new dependency, we prefer dependencies that are already packaged by Debian or Red Hat. This makes life much easier for our downstream package maintainers.

Dependencies must also be compatible with the source code license.

English style guide

pikepdf is always spelled “pikepdf”, and never capitalized even at the beginning of a sentence.

Periodic allusions to fish are required, and the writer shall be energetic and mildly amusing.

Known ports/packagers

pikepdf has been ported to many platforms already. If you are interesting in porting to a new platform, check with Repology to see the status of that platform.