Contributing
Bug reports, fixes and new features are welcome. To report a bug or suggest a feature, open an issue on GitHub. A short program or a sample PDF that shows the problem makes a report much faster to act on.
Build and test
You need the .NET SDK version named in global.json (10.0.100 or a later feature band). The test
project also runs on .NET 8, so install the .NET 8 runtime too. Every project and the solution are
under src/, so name the solution in each command; a bare dotnet test at the repository root
finds nothing to run.
dotnet build src/PdfPinata.slnx
dotnet test src/PdfPinata.slnx
dotnet test src/PdfPinata.slnx -f net10.0 # one target framework
dotnet test src/PdfPinata.slnx --filter "FullyQualifiedName~CLexerTests" # one test class
Some tests turn PDFs into images and compare them with reference images. They need Ghostscript. On
Windows it comes from a NuGet package and needs no setup. On Linux and macOS, install it with the
package manager (apt-get install ghostscript or brew install ghostscript). Tests that compare
against reference images skip themselves when Ghostscript cannot draw on the machine. CI runs on
Linux, which is the reference platform for rendering.
When Ghostscript fails inside the test host, the whole process can end. dotnet test then prints
"Test host process crashed" and a Passed! line with a total lower than the number of tests. That
run did not pass. Check the exit code, and compare the total with dotnet test --list-tests. Add
--blame-crash to find the test that did not finish, and run again before you believe it.
Run the demos
src/SampleApp is a command-line app with one demo per feature. The same demos fill the
demo gallery and the code on these pages.
dotnet run --project src/SampleApp -- list # what each demo shows
dotnet run --project src/SampleApp -- run # every demo, into ./output
dotnet run --project src/SampleApp -- run --example Fonts Text # only the demos named
dotnet run --project src/SampleApp -- run --no-code --output out # no source, another folder
A test runs every demo, so a demo that throws or changes its page count fails the build. To add a
demo, add a class to src/SampleApp/Demos/ and list it in DemoRegistry. The code between
#region example and #endregion is what the app prints and what the site quotes. A demo must not
register a backend itself, and its fonts and images are embedded resources.
Work on the documentation site
The site is Docusaurus, in docs-website/. You need Node.js 20 or later and pnpm.
cd docs-website
pnpm install
pnpm run demos # build the SampleApp and render each demo's PDF and thumbnail
pnpm start # serve the site locally, reloading on change
pnpm run demos needs the .NET SDK and Ghostscript (gs, or gswin64c on Windows). It writes into
static/demos, which is not committed. Without those tools it prints a warning and the gallery
shows placeholders; the rest of the site still works.
Quote code from the demos
Pages do not keep their own copies of code. A code block that names a demo is filled from that demo's
source when the site builds. With no snippet, it gets the demo's whole #region example. With a
snippet, it gets one excerpt:
```csharp demo=Protect snippet=encrypt
```
Leave the body of the block empty; the build replaces whatever is there. The block gets the demo's file path as its title.
To create the excerpt, add marker comments around the lines inside the demo's #region example:
// docs:begin encrypt
document.SecuritySettings.UserPassword = "user";
// docs:end encrypt
Each marker is on a line of its own, indented like the code around it. Names are kebab-case and unique within the file. The SampleApp leaves the marker lines out when it prints a demo's source. Add only marker lines; do not change the demo's code to suit the page. If a page names a demo or an excerpt that does not exist, the build fails and names the page and the file.
A page lists the demos it explains in its front matter, as demos: [Protect, Signing]. The gallery
uses that list to link each demo to its pages.
Pull requests
- Write commit subjects in plain prose, in the imperative, and describe the change in behaviour, for
example "Read a hex string by the digits in it rather than by what follows them". Do not put a
Conventional Commits prefix on a commit subject, except
chore:orrefactor:for pure housekeeping. Use the body to say what was wrong and why the new way is right. - Give the pull request title a Conventional Commits prefix:
feat:,fix:,perf:,docs:,chore:,refactor:,test:and so on, with!for a breaking change (feat!:). The prefix sets the pull request's label, and the label decides the release-note section and the version bump. - Add a test for a fix or a feature. The tests for most of the library are in
src/PdfPinata.Test. - Keep the final newline at the end of each file;
.editorconfigasks for it.
Package versions come from git tags, so do not add a <Version> to a project file.
Design notes
docs/specs/ in the repository holds a design note for each larger feature of this fork: what was
built, what was left out on purpose, and why. Read the note for an area before you extend it. They
are on GitHub.