diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..c0a1d0e --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,56 @@ +name: Build and Deploy Docs + +on: + push: + branches: [master, develop] + paths: + - 'docs/**' + - 'src/**/*.cs' + - 'src/**/*.csproj' + - '.github/workflows/docs.yml' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.x + + - name: Install DocFX + run: dotnet tool install -g docfx + + - name: Build docs + run: docfx docs/docfx.json + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/_site + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index ac850d4..bab9b51 100644 --- a/README.md +++ b/README.md @@ -6,586 +6,103 @@ [![Downloads](https://img.shields.io/nuget/dt/Gotenberg.Sharp.API.Client.svg?logo=nuget&color=purple)](https://www.nuget.org/packages/Gotenberg.Sharp.API.Client) ![Build status](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/actions/workflows/deploy.yml/badge.svg) -⭐ For Gotenberg v7 & v8 ⭐ +.NET C# client for [Gotenberg](https://gotenberg.dev/) v7 & v8 — a Docker-powered stateless API for converting & merging HTML, Markdown, and Office documents to PDF. Includes a configurable [Polly](http://www.thepollyproject.org/) retry policy with exponential backoff. -📋 [Release History & Changes](CHANGES.MD) +## Features -.NET C# Client for interacting with the [Gotenberg](https://gotenberg.dev/) v7 & v8 micro-service's API. [Gotenberg](https://github.com/gotenberg/gotenberg) is a [Docker-powered stateless API](https://hub.docker.com/r/gotenberg/gotenberg/) for converting & merging HTML, Markdown and Office documents to PDF. The client supports a configurable [Polly](http://www.thepollyproject.org/) **retry policy** with exponential backoff for handling transient exceptions. +- **HTML/URL to PDF** with Chromium (page properties, headers/footers, cookies, wait conditions) +- **Screenshots** of HTML or URLs as PNG, JPEG, or WebP +- **Office to PDF** via LibreOffice (100+ formats, image compression, watermarks) +- **PDF Manipulation** — merge, flatten, rotate, split, encrypt, watermark, stamp +- **PDF/A & PDF/UA** compliance, metadata read/write +- **Webhooks** for async PDF generation +- **DI-Ready** with Polly retry policies -# Getting Started +## Quick Start -## Using Docker Run -*Pull the image from dockerhub.com* -```powershell -> docker pull gotenberg/gotenberg:latest +```bash +docker run --rm -p 3000:3000 gotenberg/gotenberg:latest ``` -*Create & start a container* -```powershell -docker run --name gotenbee8x --rm -p 3000:3000 gotenberg/gotenberg:latest gotenberg --api-timeout=1800s --log-level=debug -``` - -## Using Docker Compose (with Basic Auth) -For local development with basic authentication enabled, use the provided docker-compose file: -```powershell -docker-compose -f docker/docker-compose-basic-auth.yml up -d +```bash +dotnet add package Gotenberg.Sharp.Api.Client ``` -Pre-configured with test credentials: -- **Username:** `testuser` -- **Password:** `testpass` - -# .NET Core Project Setup -*Install nuget package into your project* -```powershell -PM> Install-Package Gotenberg.Sharp.Api.Client -``` - -*Note: Use v1.x nugets for Gotenberg v6.* - -## IntelliSense Documentation -All public APIs include comprehensive XML documentation with clear descriptions, parameter details, and links to official Gotenberg documentation. IntelliSense provides: -- Method descriptions with Gotenberg route documentation links -- Parameter explanations and valid value ranges -- Exception documentation for error handling -- Usage notes and best practices - -## AppSettings -```json - "GotenbergSharpClient": { - "ServiceUrl": "http://localhost:3000", - "HealthCheckUrl": "http://localhost:3000/health", - "RetryPolicy": { - "Enabled": true, - "RetryCount": 4, - "BackoffPower": 1.5, - "LoggingEnabled": true - } - } -``` - -### Optional: Basic Authentication -**Gotenberg v8+** - If your Gotenberg instance is configured with basic authentication (using `--api-enable-basic-auth`), you can provide credentials in the settings: - -```json - "GotenbergSharpClient": { - "ServiceUrl": "http://localhost:3000", - "HealthCheckUrl": "http://localhost:3000/health", - "BasicAuthUsername": "your-username", - "BasicAuthPassword": "your-password", - "RetryPolicy": { - "Enabled": true, - "RetryCount": 4, - "BackoffPower": 1.5, - "LoggingEnabled": true - } - } -``` - -## Configure Services In Startup.cs - -### Basic Configuration (using appsettings.json) ```csharp -public void ConfigureServices(IServiceCollection services) -{ - ..... - services.AddOptions() - .Bind(Configuration.GetSection(nameof(GotenbergSharpClient))); - services.AddGotenbergSharpClient(); - ..... -} +// Startup.cs +services.AddOptions() + .Bind(Configuration.GetSection("GotenbergSharpClient")); +services.AddGotenbergSharpClient(); ``` -### Programmatic Configuration -```csharp -public void ConfigureServices(IServiceCollection services) -{ - ..... - // Configure with an action - services.AddOptions() - .Configure(options => - { - options.ServiceUrl = new Uri("http://localhost:3000"); - options.TimeOut = TimeSpan.FromMinutes(5); - options.BasicAuthUsername = "username"; - options.BasicAuthPassword = "password"; - // Configure retry policy - options.RetryPolicy = new RetryOptions - { - Enabled = true, - RetryCount = 4, - BackoffPower = 1.5, - LoggingEnabled = true - }; - }); - - services.AddGotenbergSharpClient(); - ..... -} -``` +### HTML to PDF -### Hybrid Configuration (appsettings + programmatic override) ```csharp -public void ConfigureServices(IServiceCollection services) -{ - ..... - services.AddOptions() - .Bind(Configuration.GetSection(nameof(GotenbergSharpClient))) - .PostConfigure(options => - { - // Override or add settings programmatically (runs after binding) - options.TimeOut = TimeSpan.FromMinutes(10); // Override timeout - options.BasicAuthUsername = Environment.GetEnvironmentVariable("GOTENBERG_USER"); - options.BasicAuthPassword = Environment.GetEnvironmentVariable("GOTENBERG_PASS"); - }); - - services.AddGotenbergSharpClient(); - ..... -} -``` - +var builder = new HtmlRequestBuilder() + .AddDocument(doc => doc.SetBody("

Hello PDF!

")) + .WithPageProperties(pp => pp.UseChromeDefaults()); -# Using GotenbergSharpClient -*See the [examples folder](examples/)* for complete working examples as console applications. - -## Required Using Statements -```csharp -using Gotenberg.Sharp.API.Client; -using Gotenberg.Sharp.API.Client.Domain.Builders; -using Gotenberg.Sharp.API.Client.Domain.Builders.Faceted; -using Gotenberg.Sharp.API.Client.Domain.Requests.Facets; // For Cookie, etc. -using Gotenberg.Sharp.API.Client.Domain.ValueObjects; // For ScreenshotFormat, etc. +var result = await sharpClient.HtmlToPdfAsync(builder); ``` -### HTML To PDF -*With embedded assets:* +### Screenshot ```csharp - [HttpGet] - public async Task HtmlToPdf([FromServices] GotenbergSharpClient sharpClient) - { - var builder = new HtmlRequestBuilder() - .AddDocument(doc => - doc.SetBody(GetBody()).SetFooter(GetFooter()) - ).WithPageProperties(pp => - { - pp.SetPaperSize(PaperSizes.A3) - .SetMargins(Margins.None) - .SetScale(.99); - }).WithAsyncAssets(async assets => assets.AddItem("some-image.jpg", await GetImageBytes())); - - var req = await builder.BuildAsync(); +var builder = new ScreenshotHtmlRequestBuilder() + .AddDocument(doc => doc.SetBody("

Screenshot!

")) + .WithScreenshotProperties(p => p.SetSize(1280, 720).SetFormat(ScreenshotFormat.Png)); - var result = await sharpClient.HtmlToPdfAsync(req); - - return this.File(result, "application/pdf", "gotenbergFromHtml.pdf"); - } +var imageStream = await sharpClient.ScreenshotHtmlAsync(builder); ``` -### URL To PDF -*URL to PDF with custom page range, header & footer:* +### Office to PDF ```csharp -public async Task CreateFromUrl(string headerPath, string footerPath) -{ - var builder = new UrlRequestBuilder() - .SetUrl("https://www.cnn.com") - .ConfigureRequest(config => - { - config.SetPageRanges("1-2"); - }) - .AddAsyncHeaderFooter(async - doc => doc.SetHeader(await File.ReadAllTextAsync(headerPath)) - .SetFooter(await File.ReadAllBytesAsync(footerPath) - )).WithPageProperties(pp => - { - pp.SetPaperSize(PaperSizes.A4) - .SetMargins(Margins.None) - .SetScale(.90) - .SetLandscape(); - }); +var builder = new MergeOfficeBuilder() + .WithAsyncAssets(async a => a.AddItems(await GetDocsAsync(sourceDir))) + .SetLibreOfficeOptions(o => o.SetQuality(85).SetExportBookmarks()) + .SetPdfOutputOptions(o => o.SetPdfFormat(PdfFormat.A2b)); - var request = await builder.BuildAsync(); - return await _sharpClient.UrlToPdfAsync(request); -} +var result = await sharpClient.MergeOfficeDocsAsync(builder); ``` -## Merge Office Docs -*Merges office documents:* -```csharp -public async Task DoOfficeMerge(string sourceDirectory) -{ - var builder = new MergeOfficeBuilder() - .WithAsyncAssets(async a => a.AddItems(await GetDocsAsync(sourceDirectory))) - .SetPdfOutputOptions(o => o.SetPdfFormat(PdfFormat.A2b)); - - var request = await builder.BuildAsync(); - return await _sharpClient.MergeOfficeDocsAsync(request); -} -``` - -### LibreOffice Conversion Options -*Fine-tune LibreOffice conversions with image compression, bookmarks, and native watermarks:* - -```csharp -public async Task ConvertWithOptions(string sourceDirectory) -{ - var builder = new MergeOfficeBuilder() - .WithAsyncAssets(async a => a.AddItems(await GetDocsAsync(sourceDirectory))) - .SetLibreOfficeOptions(o => o - .SetQuality(85) - .SetReduceImageResolution() - .SetMaxImageResolution(300) - .SetExportBookmarks() - .SetNativeWatermarkText("DRAFT")); - - var request = await builder.BuildAsync(); - return await _sharpClient.MergeOfficeDocsAsync(request); -} -``` -### Markdown to PDF -*Markdown to PDF conversion with embedded assets:* - -```csharp -public async Task CreateFromMarkdown() -{ - var builder = new HtmlRequestBuilder() - .AddAsyncDocument(async - doc => doc.SetHeader(await this.GetHeaderAsync()) - .SetBody(await GetBodyAsync()) - .SetContainsMarkdown() - .SetFooter(await GetFooterAsync()) - ).WithPageProperties(pp => - { - pp.UseChromeDefaults().SetLandscape().SetScale(.90); - }).WithAsyncAssets(async - a => a.AddItems(await GetMarkdownAssets()) - ); - - var request = await builder.BuildAsync(); - return await _sharpClient.HtmlToPdfAsync(request); -} -``` -### Working with Cookies -*Add cookies to the Chromium cookie jar for authenticated requests (v2.8.1+):* - -```csharp -public async Task CreatePdfWithCookies() -{ - var builder = new UrlRequestBuilder() - .SetUrl("https://example.com/protected") - .SetConversionBehaviors(b => - { - b.AddCookie(new Cookie - { - Name = "session_token", - Value = "abc123xyz", - Domain = "example.com", - Path = "/", - Secure = true, - HttpOnly = true, - SameSite = "Lax" - }); - }) - .WithPageProperties(pp => pp.UseChromeDefaults()); - - var request = await builder.BuildAsync(); - return await _sharpClient.UrlToPdfAsync(request); -} -``` - -### Document Metadata -*Add custom metadata to your PDFs (v2.6+):* +### PDF Operations ```csharp -public async Task CreatePdfWithMetadata() -{ - var builder = new HtmlRequestBuilder() - .AddDocument(doc => doc.SetBody("

Document with Metadata

")) - .SetPdfOutputOptions(o => o.SetMetadata(new Dictionary - { - { "Author", "John Doe" }, - { "Title", "My Document" }, - { "Subject", "Important Report" }, - { "Keywords", "report, PDF, gotenberg" } - })) - .WithPageProperties(pp => pp.UseChromeDefaults()); - - var request = await builder.BuildAsync(); - return await _sharpClient.HtmlToPdfAsync(request); -} -``` - -### PDF Format Conversion -*Convert PDFs to PDF/A formats (v2.8+):* +// Rotate +using var rotated = await sharpClient.ExecutePdfEngineAsync( + PdfEngineBuilders.Rotate(90).WithPdfs(a => a.AddItem("doc.pdf", bytes))); -```csharp -public async Task ConvertToPdfA(string pdfPath) -{ - var builder = new PdfConversionBuilder() - .WithPdfs(b => b.AddItem("document.pdf", File.ReadAllBytes(pdfPath))) - .SetPdfOutputOptions(o => o.SetPdfFormat(PdfFormat.A2b)); +// Encrypt +using var encrypted = await sharpClient.ExecutePdfEngineAsync( + PdfEngineBuilders.Encrypt("reader123", "admin456").WithPdfs(a => a.AddItem("doc.pdf", bytes))); - var request = await builder.BuildAsync(); - return await _sharpClient.ConvertPdfDocumentsAsync(request); -} +// Watermark (inline, on any conversion) +var builder = new HtmlRequestBuilder() + .AddDocument(doc => doc.SetBody(html)) + .SetWatermarkOptions(w => w.SetTextWatermark("DRAFT")); ``` -### Single Page Output -*Generate a single-page PDF from multi-page content (v2.8.1+):* +## Documentation -```csharp -public async Task CreateSinglePagePdf() -{ - var builder = new UrlRequestBuilder() - .SetUrl("https://www.example.com") - .WithPageProperties(pp => - { - pp.UseChromeDefaults() - .SetSinglePage(true); - }); +See the **[full documentation](https://changemakerstudios.github.io/GotenbergSharpApiClient/)** for: - var request = await builder.BuildAsync(); - return await _sharpClient.UrlToPdfAsync(request); -} -``` -### Webhook -*All request types support webhooks* +- [Getting Started](https://changemakerstudios.github.io/GotenbergSharpApiClient/articles/getting-started.html) — setup, configuration, DI +- [HTML & URL to PDF](https://changemakerstudios.github.io/GotenbergSharpApiClient/articles/html-and-url-to-pdf.html) — Chromium features, page properties, cookies +- [Screenshots](https://changemakerstudios.github.io/GotenbergSharpApiClient/articles/screenshots.html) — PNG/JPEG/WebP capture +- [Office Conversion](https://changemakerstudios.github.io/GotenbergSharpApiClient/articles/office-conversion.html) — LibreOffice options +- [PDF Manipulation](https://changemakerstudios.github.io/GotenbergSharpApiClient/articles/pdf-manipulation.html) — merge, rotate, split, encrypt, watermark +- [Advanced Features](https://changemakerstudios.github.io/GotenbergSharpApiClient/articles/advanced-features.html) — webhooks, value objects, multi-URL merge +- [API Reference](https://changemakerstudios.github.io/GotenbergSharpApiClient/api/) — auto-generated from XML docs -```csharp - public async Task SendUrlToWebhookEndpoint(string headerPath, string footerPath) - { - var builder = new UrlRequestBuilder() - .SetUrl("https://www.cnn.com") - .ConfigureRequest(reqBuilder => - { - reqBuilder.AddWebhook(hook => - { - hook.SetUrl("http://host.docker.internal:5000/api/your/webhookReceiver") - .SetErrorUrl("http://host.docker.internal:5000/api/your/webhookReceiver/error") - .AddExtraHeader("custom-header", "value"); - }) - .SetPageRanges("1-2"); - }) - .AddAsyncHeaderFooter(async - b => b.SetHeader(await System.IO.File.ReadAllTextAsync(headerPath)) - .SetFooter(await System.IO.File.ReadAllBytesAsync(footerPath)) - ).WithPageProperties(pp => - { - pp.SetPaperSize(PaperSizes.A4) - .SetMargins(Margins.None) - .SetScale(.90) - .SetLandscape(); - }); +## Examples - var request = await builder.BuildAsync(); +See the [examples folder](examples/) for complete working console applications. - await _sharpClient.FireWebhookAndForgetAsync(request); - } +## Release History -``` -### Merge 15 URLs to one PDF -*Builds a 30 page PDF by merging the front two pages of 15 news sites. Takes about a minute to complete* +See [CHANGES.MD](CHANGES.MD) for the full release history. -```csharp -public async Task CreateWorldNewsSummary() -{ - var sites = new[] - { - "https://www.nytimes.com", "https://www.axios.com/", "https://www.csmonitor.com", - "https://www.wsj.com", "https://www.usatoday.com", "https://www.irishtimes.com", - "https://www.lemonde.fr", "https://calgaryherald.com", "https://www.bbc.com/news/uk", - "https://www.thehindu.com", "https://www.theaustralian.com.au", - "https://www.welt.de", "https://www.cankaoxiaoxi.com", - "https://www.novinky.cz", "https://www.elobservador.com.uy" - } - .Select(u => new Uri(u)); +## License - var builders = CreateBuilders(sites); - var requests = builders.Select(b => b.Build()); - - return await ExecuteRequestsAndMerge(requests); -} - -IEnumerable CreateBuilders(IEnumerable uris) -{ - foreach (var uri in uris) - { - yield return new UrlRequestBuilder() - .SetUrl(uri) - .ConfigureRequest(req => { req.SetPageRanges("1-2"); }) - .AddHeaderFooter(docBuilder => - { - docBuilder.SetHeader(GetHeadFoot(uri.Host.Replace("www.", string.Empty).ToUpper())) - .SetFooter(GetHeadFoot(uri.ToString())); - }) - .WithPageProperties(pp => - { - pp.UseChromeDefaults() - .SetScale(.90) - .SetLandscape() - .SetMarginLeft(.5) - .SetMarginRight(.5); - }); - } - - static string GetHeadFoot(string heading) - => "

" + - heading + "

"; -} - -async Task ExecuteRequestsAndMerge(IEnumerable requests) -{ - var tasks = requests.Select(r => _sharpClient.UrlToPdfAsync(r)); - var results = await Task.WhenAll(tasks); - - var mergeBuilder = new MergeBuilder() - .WithAssets(b => { - b.AddItems(results.Select((r, i) => KeyValuePair.Create($"{i}.pdf", r))); - }); - - var request = mergeBuilder.Build(); - return await _sharpClient.MergePdfsAsync(request); -} -``` - -## Advanced Features - -### PDF/UA Support for HTML Conversions -*Enable Universal Access for accessible PDFs from HTML (v2.4+):* - -```csharp -public async Task CreateAccessiblePdf() -{ - var builder = new HtmlRequestBuilder() - .AddDocument(doc => doc.SetBody("

Accessible Document

")) - .SetPdfOutputOptions(o => o.SetPdfUa()) - .WithPageProperties(pp => pp.UseChromeDefaults()); - - var request = await builder.BuildAsync(); - return await _sharpClient.HtmlToPdfAsync(request); -} -``` - -### PDF/UA for PDF Conversions -*Enable Universal Access when converting PDFs to PDF/A (v2.4+):* - -```csharp -public async Task ConvertToAccessiblePdfA(string pdfPath) -{ - var builder = new PdfConversionBuilder() - .WithPdfs(b => b.AddItem("document.pdf", File.ReadAllBytes(pdfPath))) - .SetPdfOutputOptions(o => o.SetPdfFormat(PdfFormat.A2b).SetPdfUa()); - - var request = await builder.BuildAsync(); - return await _sharpClient.ConvertPdfDocumentsAsync(request); -} -``` - -### PDF Encryption -*Password-protect PDFs with user and owner passwords:* - -```csharp -public async Task CreateEncryptedPdf() -{ - var builder = new HtmlRequestBuilder() - .AddDocument(doc => doc.SetBody("

Confidential

")) - .SetPdfOutputOptions(o => o - .SetEncryption(userPassword: "reader123", ownerPassword: "admin456")) - .WithPageProperties(pp => pp.UseChromeDefaults()); - - var request = builder.Build(); - return await _sharpClient.HtmlToPdfAsync(request); -} -``` - -### Flatten PDFs -*Flatten PDF forms and annotations (v2.8+):* - -```csharp -public async Task FlattenPdf(string pdfPath) -{ - var builder = new PdfConversionBuilder() - .WithPdfs(b => b.AddItem("form.pdf", File.ReadAllBytes(pdfPath))) - .SetPdfOutputOptions(o => o.SetFlatten()); - - var request = await builder.BuildAsync(); - return await _sharpClient.ConvertPdfDocumentsAsync(request); -} -``` - -### Skip Network Idle Event -*Speed up conversions by skipping network idle wait (Gotenberg v8+ only):* - -```csharp -public async Task FastConversion() -{ - var builder = new UrlRequestBuilder() - .SetUrl("https://example.com") - .SetConversionBehaviors(b => b.SkipNetworkIdleEvent()) - .WithPageProperties(pp => pp.UseChromeDefaults()); - - var request = await builder.BuildAsync(); - return await _sharpClient.UrlToPdfAsync(request); -} -``` - -### Screenshot from HTML -*Capture a screenshot of HTML content as PNG, JPEG, or WebP:* - -```csharp -public async Task ScreenshotHtml() -{ - var builder = new ScreenshotHtmlRequestBuilder() - .AddDocument(doc => doc.SetBody("

Screenshot!

")) - .WithScreenshotProperties(p => p - .SetSize(1280, 720) - .SetFormat(ScreenshotFormat.Png)); - - var request = builder.Build(); - return await _sharpClient.ScreenshotHtmlAsync(request); -} -``` - -### Screenshot from URL -*Capture a screenshot of any URL:* - -```csharp -public async Task ScreenshotUrl() -{ - var builder = new ScreenshotUrlRequestBuilder() - .SetUrl("https://example.com") - .WithScreenshotProperties(p => p - .SetSize(1024, 768) - .SetFormat(ScreenshotFormat.Jpeg) - .SetQuality(90) - .SetClip()); - - var request = builder.Build(); - return await _sharpClient.ScreenshotUrlAsync(request); -} -``` - -### Custom Page Properties -*Fine-tune page dimensions and properties:* - -```csharp -public async Task CustomPageProperties() -{ - var builder = new HtmlRequestBuilder() - .AddDocument(doc => doc.SetBody("

Custom Page

")) - .WithPageProperties(pp => - { - pp.SetPaperSize(PaperSizes.Letter) - .SetMargins(Margins.Normal) - .SetScale(0.95) - .SetLandscape() - .SetPrintBackground(true) - .SetGenerateDocumentOutline(true) - .SetOmitBackground(false); - }); - - var request = await builder.BuildAsync(); - return await _sharpClient.HtmlToPdfAsync(request); -} -``` +[Apache 2.0](LICENSE) diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..0a8ff9f --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,6 @@ +_site/ +api/.manifest +api/*.yml +api/*.md +!api/index.md +obj/ diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..d8f8c7b --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,5 @@ +# API Reference + +This section contains auto-generated API documentation from the source code XML comments. + +Browse the namespace tree on the left to explore all public types, methods, and properties. diff --git a/docs/articles/advanced-features.md b/docs/articles/advanced-features.md new file mode 100644 index 0000000..c535aff --- /dev/null +++ b/docs/articles/advanced-features.md @@ -0,0 +1,99 @@ +# Advanced Features + +> **Examples:** [Webhook](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/Webhook), [UrlsToMergedPdf](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/UrlsToMergedPdf), [DIExample](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/DIExample) + +## Webhooks + +All request types support asynchronous webhook processing. Gotenberg generates the PDF and POSTs it to your webhook URL. + +```csharp +var builder = new UrlRequestBuilder() + .SetUrl("https://www.example.com") + .ConfigureRequest(req => + { + req.AddWebhook(hook => + { + hook.SetUrl("http://host.docker.internal:5000/api/webhook/pdf") + .SetErrorUrl("http://host.docker.internal:5000/api/webhook/error") + .AddExtraHeader("custom-header", "value"); + }); + req.SetPageRanges("1-2"); + }) + .WithPageProperties(pp => pp.SetPaperSize(PaperSizes.A4)); + +await sharpClient.FireWebhookAndForgetAsync(builder); +``` + +## PDF Output Options + +Available on all request types via `SetPdfOutputOptions()`: + +| Method | Description | +|--------|-------------| +| `SetPdfFormat(PdfFormat.A2b)` | Convert to PDF/A (A1b, A2b, A3b) | +| `SetPdfUa()` | Enable PDF/UA accessibility | +| `SetFlatten()` | Flatten form fields | +| `SetGenerateTaggedPdf()` | Embed logical structure tags | +| `SetMetadata(dict)` | Write XMP metadata | +| `SetEncryption(user, owner)` | Password-protect the PDF | + +## DDD Value Objects + +The client uses validated value objects to prevent invalid state at construction time. Invalid values throw immediately rather than failing at the Gotenberg API level. + +| Value Object | Constraint | Example | +|-------------|------------|---------| +| `CssSelector` | Non-empty string | `CssSelector.Create("#app")` | +| `GotenbergStatusCode` | 100-599 | `GotenbergStatusCode.Create(499)` | +| `DomainName` | Non-empty, trimmed | `DomainName.Create("cdn.example.com")` | +| `EmulatedMediaFeature` | Non-empty name + value | `EmulatedMediaFeature.PrefersColorScheme("dark")` | +| `PdfPassword` | Non-empty (redacted ToString) | `PdfPassword.Create("secret")` | +| `ImageQuality` | 1-100 | `ImageQuality.Create(85)` | +| `ImageResolution` | 75/150/300/600/1200 DPI | `ImageResolution.Dpi300` | +| `RotationAngle` | 90/180/270 | `RotationAngle.Degrees90` | +| `PageRanges` | Validated format "1-3,5" | `PageRanges.Create("1-3,5,8-10")` | +| `ScreenDimension` | Positive integer | `ScreenDimension.Create(1920)` | +| `CompressionQuality` | 0-100 | `CompressionQuality.Create(90)` | + +## Merge Multiple URLs + +Build a multi-page PDF by merging the front pages of multiple websites: + +```csharp +var sites = new[] { "https://example.com", "https://example.org" } + .Select(u => new Uri(u)); + +var tasks = sites.Select(uri => +{ + var builder = new UrlRequestBuilder() + .SetUrl(uri) + .ConfigureRequest(r => r.SetPageRanges("1-2")) + .WithPageProperties(pp => pp.UseChromeDefaults().SetLandscape()); + return sharpClient.UrlToPdfAsync(builder); +}); + +var pdfs = await Task.WhenAll(tasks); + +var mergeBuilder = new MergeBuilder() + .WithAssets(b => b.AddItems( + pdfs.Select((s, i) => KeyValuePair.Create($"{i}.pdf", s)))); + +var merged = await sharpClient.MergePdfsAsync(mergeBuilder); +``` + +## Request Configuration + +All builders support `ConfigureRequest()` for cross-cutting request settings: + +```csharp +builder.ConfigureRequest(config => +{ + config.SetTrace("my-request-id") // Custom trace ID for logs + .SetPageRanges("1-5") // Page range selection + .SetResultFileName("output.pdf"); // Output filename +}); +``` + +## Examples + +See the [examples folder](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples) for complete working console applications demonstrating each feature. diff --git a/docs/articles/getting-started.md b/docs/articles/getting-started.md new file mode 100644 index 0000000..dbee05f --- /dev/null +++ b/docs/articles/getting-started.md @@ -0,0 +1,128 @@ +# Getting Started + +> **Example:** [DIExample](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/DIExample) — Full DI setup with logging and Polly retry + +## Prerequisites + +- [Docker](https://www.docker.com/) for running Gotenberg +- [.NET 6.0+](https://dotnet.microsoft.com/) (supports .NET 5, 6, 7, 8, netstandard2.0/2.1) + +## Running Gotenberg + +### Docker Run + +```bash +docker pull gotenberg/gotenberg:latest +docker run --name gotenberg --rm -p 3000:3000 gotenberg/gotenberg:latest gotenberg --api-timeout=1800s --log-level=debug +``` + +### Docker Compose (with Basic Auth) + +```bash +docker-compose -f docker/docker-compose-basic-auth.yml up -d +``` + +Pre-configured with test credentials: `testuser` / `testpass`. + +## Install the NuGet Package + +```bash +dotnet add package Gotenberg.Sharp.Api.Client +``` + +## Configure Services + +### Basic (appsettings.json) + +```json +{ + "GotenbergSharpClient": { + "ServiceUrl": "http://localhost:3000", + "HealthCheckUrl": "http://localhost:3000/health", + "RetryPolicy": { + "Enabled": true, + "RetryCount": 4, + "BackoffPower": 1.5, + "LoggingEnabled": true + } + } +} +``` + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + services.AddOptions() + .Bind(Configuration.GetSection("GotenbergSharpClient")); + services.AddGotenbergSharpClient(); +} +``` + +### With Basic Authentication + +```json +{ + "GotenbergSharpClient": { + "ServiceUrl": "http://localhost:3000", + "BasicAuthUsername": "your-username", + "BasicAuthPassword": "your-password" + } +} +``` + +### Programmatic Configuration + +```csharp +services.AddOptions() + .Configure(options => + { + options.ServiceUrl = new Uri("http://localhost:3000"); + options.TimeOut = TimeSpan.FromMinutes(5); + options.BasicAuthUsername = "username"; + options.BasicAuthPassword = "password"; + options.RetryPolicy = new RetryOptions + { + Enabled = true, + RetryCount = 4, + BackoffPower = 1.5, + LoggingEnabled = true + }; + }); +services.AddGotenbergSharpClient(); +``` + +### Hybrid (appsettings + overrides) + +```csharp +services.AddOptions() + .Bind(Configuration.GetSection("GotenbergSharpClient")) + .PostConfigure(options => + { + options.BasicAuthUsername = Environment.GetEnvironmentVariable("GOTENBERG_USER"); + options.BasicAuthPassword = Environment.GetEnvironmentVariable("GOTENBERG_PASS"); + }); +services.AddGotenbergSharpClient(); +``` + +## Required Using Statements + +```csharp +using Gotenberg.Sharp.API.Client; +using Gotenberg.Sharp.API.Client.Domain.Builders; +using Gotenberg.Sharp.API.Client.Domain.Builders.Faceted; +using Gotenberg.Sharp.API.Client.Domain.Requests.Facets; +using Gotenberg.Sharp.API.Client.Domain.ValueObjects; +``` + +## Your First PDF + +```csharp +public async Task CreatePdf([FromServices] GotenbergSharpClient sharpClient) +{ + var builder = new HtmlRequestBuilder() + .AddDocument(doc => doc.SetBody("

Hello PDF!

")) + .WithPageProperties(pp => pp.UseChromeDefaults()); + + return await sharpClient.HtmlToPdfAsync(builder); +} +``` diff --git a/docs/articles/html-and-url-to-pdf.md b/docs/articles/html-and-url-to-pdf.md new file mode 100644 index 0000000..554392a --- /dev/null +++ b/docs/articles/html-and-url-to-pdf.md @@ -0,0 +1,142 @@ +# HTML & URL to PDF + +> **Examples:** [HtmlConvert](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/HtmlConvert), [UrlConvert](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/UrlConvert), [HtmlWithMarkdown](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/HtmlWithMarkdown), [ChromiumFeatures](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/ChromiumFeatures) + +## HTML to PDF + +Convert HTML content with embedded assets, headers, and footers. + +```csharp +var builder = new HtmlRequestBuilder() + .AddDocument(doc => + doc.SetBody(GetBody()).SetFooter(GetFooter())) + .WithPageProperties(pp => + { + pp.SetPaperSize(PaperSizes.A3) + .SetMargins(Margins.None) + .SetScale(.99); + }) + .WithAsyncAssets(async assets => + assets.AddItem("some-image.jpg", await GetImageBytes())); + +var result = await sharpClient.HtmlToPdfAsync(builder); +``` + +## URL to PDF + +Convert any URL with custom page ranges, headers, and footers. + +```csharp +var builder = new UrlRequestBuilder() + .SetUrl("https://www.example.com") + .ConfigureRequest(config => config.SetPageRanges("1-2")) + .AddAsyncHeaderFooter(async doc => + doc.SetHeader(await File.ReadAllTextAsync(headerPath)) + .SetFooter(await File.ReadAllBytesAsync(footerPath))) + .WithPageProperties(pp => + { + pp.SetPaperSize(PaperSizes.A4) + .SetMargins(Margins.None) + .SetScale(.90) + .SetLandscape(); + }); + +var result = await sharpClient.UrlToPdfAsync(builder); +``` + +## Markdown to PDF + +```csharp +var builder = new HtmlRequestBuilder() + .AddAsyncDocument(async doc => + doc.SetHeader(await GetHeaderAsync()) + .SetBody(await GetBodyAsync()) + .SetContainsMarkdown() + .SetFooter(await GetFooterAsync())) + .WithPageProperties(pp => + pp.UseChromeDefaults().SetLandscape().SetScale(.90)) + .WithAsyncAssets(async a => + a.AddItems(await GetMarkdownAssets())); + +var result = await sharpClient.HtmlToPdfAsync(builder); +``` + +## Chromium Rendering Behaviors + +### Wait for Selector & Media Features + +Control Chromium's rendering with wait conditions, media emulation, and error handling. + +```csharp +var builder = new HtmlRequestBuilder() + .AddDocument(doc => doc.SetBody(html)) + .SetConversionBehaviors(b => b + // Wait for a DOM element before converting + .SetWaitForSelector("#app") + // Emulate dark mode + .AddEmulatedMediaFeature("prefers-color-scheme", "dark") + // Strict error handling + .SetFailOnHttpStatusCodes(499, 599) + .FailOnResourceLoadingFailed() + .FailOnConsoleExceptions() + // Ignore specific CDN domains for status checks + .AddIgnoreResourceHttpStatusDomains("cdn.example.com", "fonts.googleapis.com")); +``` + +### Cookies + +```csharp +builder.SetConversionBehaviors(b => b + .AddCookie(new Cookie + { + Name = "session_token", + Value = "abc123xyz", + Domain = "example.com", + Path = "/", + Secure = true, + HttpOnly = true, + SameSite = "Lax" + })); +``` + +### Custom HTTP Headers + +```csharp +builder.SetConversionBehaviors(b => b + .AddAdditionalHeaders("Authorization", "Bearer token123")); +``` + +## Page Properties + +### Paper Sizes & Margins + +```csharp +builder.WithPageProperties(pp => +{ + pp.SetPaperSize(PaperSizes.A4) // or Letter, A3, etc. + .SetMargins(Margins.Normal) // or None, Large + .SetScale(0.95) + .SetLandscape() + .SetPrintBackground(true) + .SetGenerateDocumentOutline(true); +}); +``` + +### Single Page Output + +Force all content onto one very long page: + +```csharp +builder.WithPageProperties(pp => pp.UseChromeDefaults().SetSinglePage(true)); +``` + +### Custom Dimensions + +```csharp +builder.WithPageProperties(pp => +{ + pp.SetPaperWidth(Dimension.FromInches(8.5)) + .SetPaperHeight(Dimension.FromInches(14)) + .SetMargins("1in 0.5in"); // top/bottom, left/right +}); +``` diff --git a/docs/articles/office-conversion.md b/docs/articles/office-conversion.md new file mode 100644 index 0000000..6759a2f --- /dev/null +++ b/docs/articles/office-conversion.md @@ -0,0 +1,102 @@ +# Office Document Conversion + +> **Examples:** [OfficeMerge](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/OfficeMerge), [LibreOfficeOptions](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/LibreOfficeOptions) + +Convert and merge Office documents (Word, Excel, PowerPoint, and 100+ formats) to PDF using LibreOffice. + +## Basic Merge + +```csharp +var builder = new MergeOfficeBuilder() + .WithAsyncAssets(async a => a.AddItems(await GetDocsAsync(sourceDirectory))) + .SetPdfOutputOptions(o => o.SetPdfFormat(PdfFormat.A2b)); + +var result = await sharpClient.MergeOfficeDocsAsync(builder); +``` + +## LibreOffice Options + +Fine-tune conversions with LibreOffice-specific options: + +```csharp +var builder = new MergeOfficeBuilder() + .WithAsyncAssets(async a => a.AddItems(await GetDocsAsync(sourceDirectory))) + .SetLibreOfficeOptions(o => o + // Image compression + .SetQuality(85) // JPEG quality 1-100 + .SetReduceImageResolution() + .SetMaxImageResolution(300) // DPI: 75, 150, 300, 600, 1200 + // Export options + .SetExportBookmarks() + .SetExportFormFields(false) // Flatten form fields + .SetUpdateIndexes() + // Native watermark + .SetNativeWatermarkText("DRAFT") + .SetNativeWatermarkFontName("Arial") + .SetNativeWatermarkFontHeight(48)) + .SetPdfOutputOptions(o => o.SetPdfFormat(PdfFormat.A2b)); +``` + +## All LibreOffice Options + +### Layout + +| Method | Description | Default | +|--------|-------------|---------| +| `SetSinglePageSheets()` | One page per sheet | false | +| `SetSkipEmptyPages()` | Skip empty pages (Writer only) | false | +| `SetExportPlaceholders()` | Render placeholders as visual markers | false | + +### Image Compression + +| Method | Description | Default | +|--------|-------------|---------| +| `SetLosslessImageCompression()` | Use PNG instead of JPEG | false | +| `SetQuality(1-100)` | JPEG quality | 90 | +| `SetReduceImageResolution()` | Reduce to max DPI | false | +| `SetMaxImageResolution(dpi)` | Max DPI (75/150/300/600/1200) | 300 | + +### Notes & Slides + +| Method | Description | Default | +|--------|-------------|---------| +| `SetExportNotes()` | Export notes to PDF | false | +| `SetExportNotesPages()` | Export notes pages (Impress) | false | +| `SetExportOnlyNotesPages()` | Export only notes pages | false | +| `SetExportNotesInMargin()` | Notes in margin | false | +| `SetExportHiddenSlides()` | Include hidden slides | false | + +### Links & Outline + +| Method | Description | Default | +|--------|-------------|---------| +| `SetConvertOooTargetToPdfTarget()` | Convert .od* links to .pdf | false | +| `SetExportLinksRelativeFsys()` | Relative file:// links | false | +| `SetUpdateIndexes()` | Update TOC before conversion | true | +| `SetExportBookmarks()` | Export bookmarks | true | +| `SetExportBookmarksToPdfDestination()` | Bookmarks as Named Destinations | false | +| `SetAddOriginalDocumentAsStream()` | Embed original document | false | + +### Form Fields + +| Method | Description | Default | +|--------|-------------|---------| +| `SetExportFormFields(bool)` | Export as interactive widgets | true | +| `SetAllowDuplicateFieldNames()` | Allow duplicate field names | false | + +### Native Watermark + +| Method | Description | +|--------|-------------| +| `SetNativeWatermarkText(text)` | Single-line text watermark | +| `SetNativeTiledWatermarkText(text)` | Repeating tiled watermark | +| `SetNativeWatermarkColor(rgbDecimal)` | RGB color as decimal (default: 8388223) | +| `SetNativeWatermarkFontName(font)` | Font name (default: Helvetica) | +| `SetNativeWatermarkFontHeight(pts)` | Font size in points (0 = auto) | +| `SetNativeWatermarkRotateAngle(angle)` | Rotation in tenths of degrees | + +### Source File Password + +```csharp +builder.SetLibreOfficeOptions(o => o.SetPassword("document-password")); +``` diff --git a/docs/articles/pdf-manipulation.md b/docs/articles/pdf-manipulation.md new file mode 100644 index 0000000..f62db77 --- /dev/null +++ b/docs/articles/pdf-manipulation.md @@ -0,0 +1,164 @@ +# PDF Manipulation + +> **Examples:** [PdfEngineOperations](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/PdfEngineOperations), [WatermarkAndRotate](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/WatermarkAndRotate), [EncryptPdf](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/EncryptPdf), [PdfMerge](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/PdfMerge), [PdfConvert](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/PdfConvert) + +Standalone operations on existing PDF files using Gotenberg's PDF engine routes. + +## Standalone PDF Operations + +Use `PdfEngineBuilders` factory methods to create typed builders for each operation. + +### Flatten + +Merge form fields into static content: + +```csharp +using var result = await sharpClient.ExecutePdfEngineAsync( + PdfEngineBuilders.Flatten() + .WithPdfs(a => a.AddItem("form.pdf", pdfBytes))); +``` + +### Rotate + +Rotate pages by 90, 180, or 270 degrees: + +```csharp +// Rotate all pages +using var result = await sharpClient.ExecutePdfEngineAsync( + PdfEngineBuilders.Rotate(90) + .WithPdfs(a => a.AddItem("doc.pdf", pdfBytes))); + +// Rotate specific pages +using var result = await sharpClient.ExecutePdfEngineAsync( + PdfEngineBuilders.Rotate(180, "1-3") + .WithPdfs(a => a.AddItem("doc.pdf", pdfBytes))); +``` + +### Split + +Split PDFs into chunks or extract page ranges: + +```csharp +// Split every 2 pages (returns ZIP) +using var result = await sharpClient.ExecutePdfEngineAsync( + PdfEngineBuilders.Split(SplitMode.Intervals, "2") + .WithPdfs(a => a.AddItem("doc.pdf", pdfBytes))); + +// Extract pages and unify into one PDF +using var result = await sharpClient.ExecutePdfEngineAsync( + PdfEngineBuilders.Split(SplitMode.Pages, "1-3,5", unify: true) + .WithPdfs(a => a.AddItem("doc.pdf", pdfBytes))); +``` + +### Encrypt + +Password-protect PDFs: + +```csharp +using var result = await sharpClient.ExecutePdfEngineAsync( + PdfEngineBuilders.Encrypt(userPassword, ownerPassword) + .WithPdfs(a => a.AddItem("doc.pdf", pdfBytes))); +``` + +### Write Metadata + +```csharp +using var result = await sharpClient.ExecutePdfEngineAsync( + PdfEngineBuilders.WriteMetadata(new Dictionary + { + { "Author", "John Doe" }, + { "Title", "My Document" } + }).WithPdfs(a => a.AddItem("doc.pdf", pdfBytes))); +``` + +### Read Metadata + +Returns JSON keyed by filename: + +```csharp +var json = await sharpClient.ReadPdfMetadataAsync( + PdfEngineBuilders.ReadMetadata() + .WithPdfs(a => a.AddItem("doc.pdf", pdfBytes))); + +// json: { "doc.pdf": { "Author": "...", "Title": "...", ... } } +``` + +## Cross-Cutting Options + +These options are available on **all** request types (HTML, URL, Office, PDF conversion) via `BuildRequestBase`. + +### Watermark + +Add a background overlay: + +```csharp +var builder = new HtmlRequestBuilder() + .AddDocument(doc => doc.SetBody(html)) + .SetWatermarkOptions(w => w + .SetTextWatermark("CONFIDENTIAL") // text watermark on all pages + // Or target specific pages: + // .SetTextWatermark("DRAFT", "1-3") + ); +``` + +### Stamp + +Add a foreground overlay (same API as watermark): + +```csharp +builder.SetStampOptions(s => s.SetTextStamp("APPROVED", "1")); +``` + +### Rotation + +Rotate pages inline during conversion: + +```csharp +builder.SetRotationOptions(r => r + .SetAngle(90) // 90, 180, or 270 + .SetPages("2-3")); // optional: specific pages +``` + +### Split + +Split output during conversion: + +```csharp +builder.SetSplitOptions(s => s.SplitByPages("1-3,5", unify: true)); +``` + +## PDF Format Conversion + +Convert existing PDFs to PDF/A or apply transformations: + +```csharp +var builder = new PdfConversionBuilder() + .WithPdfs(b => b.AddItem("document.pdf", File.ReadAllBytes(pdfPath))) + .SetPdfOutputOptions(o => o + .SetPdfFormat(PdfFormat.A2b) + .SetPdfUa() + .SetFlatten()); + +var result = await sharpClient.ConvertPdfDocumentsAsync(builder); +``` + +## PDF Encryption (Inline) + +Encrypt PDFs generated from any conversion route: + +```csharp +var builder = new HtmlRequestBuilder() + .AddDocument(doc => doc.SetBody(html)) + .SetPdfOutputOptions(o => o + .SetEncryption(userPassword: "reader", ownerPassword: "admin")); +``` + +## Merge PDFs + +```csharp +var mergeBuilder = new MergeBuilder() + .WithAssets(b => b.AddItems(pdfStreams.Select((s, i) => + KeyValuePair.Create($"{i}.pdf", s)))); + +var result = await sharpClient.MergePdfsAsync(mergeBuilder); +``` diff --git a/docs/articles/screenshots.md b/docs/articles/screenshots.md new file mode 100644 index 0000000..a80577f --- /dev/null +++ b/docs/articles/screenshots.md @@ -0,0 +1,61 @@ +# Screenshots + +> **Example:** [Screenshot](https://github.com/ChangemakerStudios/GotenbergSharpApiClient/tree/develop/examples/Screenshot) + +Capture screenshots of HTML content or URLs using Chromium. Returns PNG, JPEG, or WebP images. + +## Screenshot from HTML + +```csharp +var builder = new ScreenshotHtmlRequestBuilder() + .AddDocument(doc => doc.SetBody(@" + +

Screenshot Demo

+ ")) + .WithScreenshotProperties(p => p + .SetSize(1280, 720) + .SetFormat(ScreenshotFormat.Png)); + +var imageStream = await sharpClient.ScreenshotHtmlAsync(builder); +``` + +## Screenshot from URL + +```csharp +var builder = new ScreenshotUrlRequestBuilder() + .SetUrl("https://example.com") + .WithScreenshotProperties(p => p + .SetSize(1024, 768) + .SetFormat(ScreenshotFormat.Jpeg) + .SetQuality(90) + .SetClip()); + +var imageStream = await sharpClient.ScreenshotUrlAsync(builder); +``` + +## Screenshot Properties + +| Property | Description | Default | +|----------|-------------|---------| +| `SetSize(width, height)` | Device screen dimensions in pixels | 800x600 | +| `SetFormat(format)` | `Png`, `Jpeg`, or `Webp` | Png | +| `SetQuality(0-100)` | Compression quality (JPEG only) | 100 | +| `SetClip()` | Clip to device dimensions | false | +| `SetOmitBackground()` | Transparent background (PNG/WebP) | false | +| `SetOptimizeForSpeed()` | Faster encoding, larger files | false | + +## With Conversion Behaviors + +Screenshots share the same Chromium rendering behaviors as PDF conversions: + +```csharp +var builder = new ScreenshotHtmlRequestBuilder() + .AddDocument(doc => doc.SetBody(html)) + .WithScreenshotProperties(p => p + .SetSize(1920, 1080) + .SetFormat(ScreenshotFormat.Png)) + .SetConversionBehaviors(b => b + .SetWaitForSelector("#loaded") + .AddEmulatedMediaFeature("prefers-color-scheme", "dark") + .SkipNetworkIdleEvent()); +``` diff --git a/docs/articles/toc.yml b/docs/articles/toc.yml new file mode 100644 index 0000000..4c03ceb --- /dev/null +++ b/docs/articles/toc.yml @@ -0,0 +1,12 @@ +- name: Getting Started + href: getting-started.md +- name: HTML & URL to PDF + href: html-and-url-to-pdf.md +- name: Screenshots + href: screenshots.md +- name: Office Document Conversion + href: office-conversion.md +- name: PDF Manipulation + href: pdf-manipulation.md +- name: Advanced Features + href: advanced-features.md diff --git a/docs/docfx.json b/docs/docfx.json new file mode 100644 index 0000000..9e9ce5b --- /dev/null +++ b/docs/docfx.json @@ -0,0 +1,50 @@ +{ + "metadata": [ + { + "src": [ + { + "files": ["**/*.csproj"], + "src": "../src" + } + ], + "dest": "api", + "properties": { + "TargetFramework": "net8.0" + } + } + ], + "build": { + "content": [ + { + "files": ["api/**/*.yml", "api/index.md"] + }, + { + "files": [ + "articles/**/*.md", + "articles/**/toc.yml", + "toc.yml", + "*.md" + ] + } + ], + "resource": [ + { + "files": ["images/**"] + } + ], + "output": "_site", + "template": ["default", "modern"], + "globalMetadata": { + "_appTitle": "GotenbergSharpApiClient", + "_appName": "GotenbergSharpApiClient", + "_appFooter": "GotenbergSharpApiClient - C# Client for Gotenberg API", + "_enableSearch": true, + "_appLogoPath": "images/logo.png", + "_disableContribution": false, + "_gitContribute": { + "repo": "https://github.com/ChangemakerStudios/GotenbergSharpApiClient", + "branch": "develop" + } + } + } +} diff --git a/docs/images/logo.png b/docs/images/logo.png new file mode 100644 index 0000000..81203b0 Binary files /dev/null and b/docs/images/logo.png differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..96876dd --- /dev/null +++ b/docs/index.md @@ -0,0 +1,48 @@ +--- +_layout: landing +--- + +# GotenbergSharpApiClient + +.NET C# client for [Gotenberg](https://gotenberg.dev/) — a Docker-powered stateless API for converting & merging HTML, Markdown, and Office documents to PDF. + +## Features + +- **HTML/URL to PDF** — Convert HTML content or URLs to PDF using Chromium +- **Screenshots** — Capture screenshots of HTML or URLs as PNG, JPEG, or WebP +- **Office to PDF** — Convert Word, Excel, PowerPoint (100+ formats) via LibreOffice +- **PDF Manipulation** — Merge, flatten, rotate, split, encrypt, watermark, stamp +- **PDF/A & PDF/UA** — Archive-ready and accessible PDF output +- **Metadata** — Read and write PDF metadata +- **Webhooks** — Async PDF generation with callback support +- **DI-Ready** — Built for dependency injection with Polly retry policies + +## Quick Start + +```bash +dotnet add package Gotenberg.Sharp.Api.Client +``` + +```csharp +services.AddOptions() + .Bind(Configuration.GetSection("GotenbergSharpClient")); +services.AddGotenbergSharpClient(); +``` + +```csharp +var builder = new HtmlRequestBuilder() + .AddDocument(doc => doc.SetBody("

Hello PDF!

")) + .WithPageProperties(pp => pp.UseChromeDefaults()); + +var result = await sharpClient.HtmlToPdfAsync(builder); +``` + +## Documentation + +- [Getting Started](articles/getting-started.md) +- [HTML & URL to PDF](articles/html-and-url-to-pdf.md) +- [Screenshots](articles/screenshots.md) +- [Office Document Conversion](articles/office-conversion.md) +- [PDF Manipulation](articles/pdf-manipulation.md) +- [Advanced Features](articles/advanced-features.md) +- [API Reference](api/index.md) diff --git a/docs/toc.yml b/docs/toc.yml new file mode 100644 index 0000000..965b409 --- /dev/null +++ b/docs/toc.yml @@ -0,0 +1,4 @@ +- name: Articles + href: articles/ +- name: API Reference + href: api/