Contributing to TerraPDF
Thank you for taking the time to contribute! This document explains how to get started, what we expect from contributions, and how the release process works.
Table of Contents
- Code of Conduct
- Getting Started
- Project Structure
- Making Changes
- Coding Standards
- Tests
- Submitting a Pull Request
- Reporting Bugs
- Suggesting Features
- Release Process
Code of Conduct
Be respectful, constructive, and welcoming. We will not tolerate harassment or discrimination in any form.
Getting Started
Prerequisites
- .NET 8 SDK or later (the library targets both .NET 8 and .NET 9)
- Git
Clone and Build
git clone https://github.com/sahebansari/TerraPDF.git
cd TerraPDF
dotnet restore
dotnet build
dotnet test
Project Structure
TerraPDF/
├── src/
│ └── TerraPDF/
│ ├── Core/ # Fluent API — descriptors, extension methods, Document entry point
│ ├── Drawing/ # PDF rendering — PdfDocument, PdfPage, FontMetrics, image decoders
│ ├── Elements/ # Internal layout tree — Column, Row, Table, TextBlock, decorators
│ ├── Helpers/ # Public helpers — Color, PageSize, TextStyle, Unit
│ └── Infra/ # Public interfaces — IContainer, IDocument, IComponent
├── tests/
│ └── TerraPDF.Tests/ # xUnit test projects
├── samples/
│ └── TerraPDF.Sample/ # Six sample PDFs covering all major features
├── docs/ # Markdown documentation
├── .github/workflows/ # CI and publish workflows
├── Directory.Build.props # Shared MSBuild properties (nullable, warnings, etc.)
└── .editorconfig # Code style rules
Making Changes
-
Fork the repository and create a branch from
main:git checkout -b feature/my-feature -
Make your changes. Keep commits focused and atomic.
-
Ensure all existing tests still pass and add new tests for any behaviour you introduce or change.
-
Run the full test suite before pushing:
dotnet test -c Release -
Open a Pull Request against
main.
Coding Standards
All standards are enforced automatically by the build:
- Nullable reference types are enabled (
<Nullable>enable</Nullable>) — no nullable warnings are accepted. - Warnings as errors — the build fails on any warning.
.editorconfig— code style (indentation, naming, var usage, etc.) is enforced at build time via<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>.- XML doc comments are required on all public members.
- Input validation — every public API method that takes user-supplied
arguments must guard them with the appropriate
ArgumentNullException.ThrowIfNull,ArgumentException.ThrowIfNullOrWhiteSpace, orArgumentOutOfRangeException.ThrowIfNegative/ThrowIfNegativeOrZerocall (all available since .NET 8). - No third-party dependencies in the library project — TerraPDF has zero runtime dependencies and this must remain true.
Tests
Tests live in tests/TerraPDF.Tests/ and use xUnit.
| File | What it tests |
|---|---|
DocumentGenerationTests.cs |
Integration — produces valid PDF bytes |
FontMetricsTests.cs |
Unit — glyph-width accuracy against Adobe AFM values |
TextStyleTests.cs |
Unit — TextStyle immutability and merge logic |
ValidationTests.cs |
Unit — every public method throws the right exception on bad input |
BehaviourTests.cs |
Integration — layout, formatting, decorator, and component behaviour |
HighPriorityFeatureTests.cs |
Integration — underline, line-height, hyperlink, per-edge borders |
RoundedBorderTests.cs |
Unit/Integration — RoundedBorder and RoundedBox geometry and validation |
PageBreakTests.cs |
Integration — explicit page-break positioning |
HeaderFirstPageOnlyTests.cs |
Integration — conditional first-page-only header rendering |
Running with coverage
dotnet test -c Release --collect:"XPlat Code Coverage"
Coverage reports (Cobertura XML) are written to TestResults/.
Submitting a Pull Request
- Fill in the PR template with a clear description of what changed and why.
- Reference any related issues (e.g.
Closes #42). - All CI checks must pass before a review is requested.
- At least one maintainer approval is required before merging.
Reporting Bugs
Open a GitHub Issue and include:
- TerraPDF version
- .NET runtime version
- A minimal reproducible code snippet
- The expected vs. actual behaviour
Suggesting Features
Open a GitHub Issue with the
label enhancement. Describe the use-case, not just the solution.
Release Process
- Bump
<Version>insrc/TerraPDF/TerraPDF.csproj. - Update
CHANGELOG.md— move items from[Unreleased]to a new versioned section. - Commit and push to
main. - Wait for CI to go green (build-and-test + pack dry-run must pass)
- Create a GitHub Release with a tag matching the version (e.g.
v1.3.0).- The
publish.ymlworkflow automatically packs and pushes the.nupkgand.snupkg(symbols) to nuget.org.
- The
- Verify on nuget.org.
Renewing the API Key
nuget.org API keys expire. Before expiry:
- Sign in to nuget.org → API Keys → Edit → Regenerate.
- Copy the new key.
- Update the
NUGET_API_KEYGitHub secret (Settings → Secrets → Actions).
Troubleshooting
| Symptom | Fix |
|---|---|
| Workflow fails: "tag does not match csproj version" | Ensure the GitHub Release tag (e.g. v1.3.0) matches <Version>1.3.0</Version> in the csproj exactly. |
403 Forbidden from nuget.org |
API key expired or has insufficient scope. Regenerate and update the secret. |
| Package visible but README is blank on nuget.org | Verify README.md is included via <PackageReadmeFile>README.md</PackageReadmeFile> and the <None Include=... Pack="true"> item. |
| Source Link warning locally | Expected when building outside a git repo. The warning disappears on GitHub Actions where fetch-depth: 0 is used. |
Old .nupkg in artifacts/ |
The folder is gitignored. Delete it with Remove-Item artifacts/ -Recurse before a fresh pack. |