Skip to content

Commit 0b49463

Browse files
Merge pull request #113 from datalogics-kam/pdfcloud-5217-runnable-csharp-samples-part-1
PDFCLOUD-5217 Runnable C# samples, part 1
2 parents 98afe07 + e09c739 commit 0b49463

111 files changed

Lines changed: 8090 additions & 3227 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

DotNET/.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copy to ".env" and set your values.
2+
3+
# Required: pdfRest API key
4+
PDFREST_API_KEY=your_api_key_here
5+
6+
# Optional: API base URL
7+
# - Default (US): https://api.pdfrest.com
8+
# - EU/GDPR region: https://eu-api.pdfrest.com
9+
PDFREST_URL=https://api.pdfrest.com
10+
# For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work

DotNET/AGENTS.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Repository Guidelines
2+
3+
## Project Structure & Module Organization
4+
- DotNET samples are organized by scenario:
5+
- `Endpoint Examples/Multipart Payload`: send files + params via `multipart/form-data`.
6+
- `Endpoint Examples/JSON Payload`: upload files then call endpoints with JSON.
7+
- `Complex Flow Examples`: multi‑step workflows across endpoints.
8+
- Recommended: a single .NET 8 console app project that dispatches to these samples via a command.
9+
10+
## Build, Run, and Environment
11+
- Prereq: .NET 8 SDK (`dotnet --version`).
12+
- Included here:
13+
- `DotNetSamples.csproj` wired to `Program.cs` (dispatcher).
14+
- `.env.example` template for local configuration.
15+
- Environment config (mirrors VB.NET):
16+
- Copy `.env.example` to `.env` and set `PDFREST_API_KEY=...`.
17+
- Optional: `PDFREST_URL=https://eu-api.pdfrest.com` for EU/GDPR; default is `https://api.pdfrest.com`.
18+
- Run commands from this folder:
19+
- `dotnet build`
20+
- `dotnet run -- markdown-json /path/to/input.pdf`
21+
- `dotnet run -- rasterized-pdf /path/to/input.pdf`
22+
- `dotnet run -- pdf-multipart /path/to/input.html text/html`
23+
- `dotnet run -- merge-different-file-types image.png slides.ppt`
24+
- `dotnet run -- extracted-text /path/to/input.pdf`
25+
- `dotnet run -- extracted-images /path/to/input.pdf`
26+
- `dotnet run -- pdf-info /path/to/input.pdf`
27+
- `dotnet run -- merged-pdf file1.pdf file2.pdf`
28+
- `dotnet run -- split-pdf /path/to/input.pdf`
29+
- `dotnet run -- upload /path/to/input.pdf`
30+
- `dotnet run -- get-resource <id> [out]`
31+
- `dotnet run -- delete-resource <id>`
32+
- `dotnet run -- batch-delete <id1> [id2] [...]`
33+
- `.env` loading: uses `DotNetEnv` (`Env.Load()`), matching VB.NET.
34+
35+
Note: The project currently compiles only `Program.cs` and `Endpoint Examples/JSON Payload/markdown.cs` to avoid top‑level statement conflicts. To enable more samples, add them explicitly to `DotNetSamples.csproj` under a `<Compile Include="..." />` item or refactor them into classes with `Execute(string[] args)`.
36+
37+
Why no solution file?
38+
- This folder contains a single project; removing the `.sln` lets `dotnet build` and `dotnet run` infer the project automatically. If you later add multiple projects, reintroduce a solution or pass `--project` explicitly.
39+
40+
## Coding Style & Naming Conventions
41+
- C# 12 / .NET 8; 4‑space indentation; minimal, single‑responsibility methods.
42+
- Filenames: kebab‑case describing action (e.g., `pdf-with-ocr-text.cs`).
43+
- HTTP: use `HttpClient` with `Api-Key` header; base URL from `PDFREST_URL` to support region selection.
44+
- Prefer low-indentation patterns:
45+
- File-scoped namespaces (`namespace X.Y;`).
46+
- `using var` for disposables instead of nested `using (...) {}` blocks.
47+
48+
## Testing Guidelines
49+
- No unit tests here; validate by running commands and inspecting JSON output.
50+
- For binary endpoints, write bytes to disk and manually verify (open PDF/image).
51+
52+
## Adapting Samples (Minimal Changes)
53+
- Keep the original I/O style: if a sample uses sync calls, keep them sync; if it uses async/await, keep it async.
54+
- Wrap in a small shim so Program.cs can call it:
55+
- `namespace Samples.<FolderPath>;`
56+
- `public static class <SampleName> { public static <void|Task> Execute(string[] args) { /* existing code */ } }`
57+
- Replace literals with environment/args only:
58+
- API key: `Environment.GetEnvironmentVariable("PDFREST_API_KEY")`
59+
- Base URL: `Environment.GetEnvironmentVariable("PDFREST_URL") ?? "https://api.pdfrest.com"`
60+
- Input path: take from `args[0]` and use for `Content-Filename`.
61+
- Opt the file into the project by adding `<Compile Include="…" />` to `DotNetSamples.csproj`.
62+
- Always insert the Sample Header Template at the very beginning of the file (before any `using` lines).
63+
64+
## Sample Header Template
65+
Use this comment block at the TOP of each sample file you adapt for dispatching — place it before any `using` directives or namespace (keep lines concise and include the GDPR info line):
66+
67+
```
68+
/*
69+
* What this sample does:
70+
* - Brief description… (how it’s called from Program.cs)
71+
*
72+
* Setup (environment):
73+
* - Copy .env.example to .env
74+
* - Set PDFREST_API_KEY=your_api_key_here
75+
* - Optional: set PDFREST_URL to override the API region. For EU/GDPR compliance and proximity, use:
76+
* PDFREST_URL=https://eu-api.pdfrest.com
77+
* For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
78+
*
79+
* Usage:
80+
* dotnet run -- <command> [args]
81+
*
82+
* Output:
83+
* - Describe output and error behavior.
84+
*/
85+
```
86+
87+
## Commit & Pull Request Guidelines
88+
- Commits: concise, scoped to a sample or infra. Prefixes: `feat(sample)`, `fix(sample)`, `chore(env)`, `docs(dotnet)`.
89+
- PRs include: purpose, endpoints, run example (`dotnet run -- <command>`), assumptions (paths/content types), and sample output.
90+
91+
## Security & Region/GDPR Tips
92+
- Never commit secrets or `.env`; provide `.env.example` only.
93+
- Prefer `DotNetEnv` + `Environment.GetEnvironmentVariable` for keys/URLs.
94+
- EU/GDPR: set `PDFREST_URL=https://eu-api.pdfrest.com` to keep data in-region; default is `https://api.pdfrest.com`.
Lines changed: 111 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,116 @@
1+
/*
2+
* What this sample does:
3+
* - Decrypts a PDF, adds an image, then re-encrypts it.
4+
* - Routed from Program.cs as: `dotnet run -- decrypt-add-reencrypt <pdf> <image> [password]`.
5+
*
6+
* Setup (environment):
7+
* - Copy .env.example to .env
8+
* - Set PDFREST_API_KEY=your_api_key_here
9+
* - Optional: set PDFREST_URL to override the API region. For EU/GDPR compliance and proximity, use:
10+
* PDFREST_URL=https://eu-api.pdfrest.com
11+
* For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
12+
*
13+
* Usage:
14+
* dotnet run -- decrypt-add-reencrypt input.pdf image.png secret
15+
*
16+
* Output:
17+
* - Prints JSON responses for decrypt, add-image, and re-encrypt stages.
18+
*/
19+
120
using Newtonsoft.Json.Linq;
221
using System.Text;
322

4-
/* In this sample, we will show how to take an encrypted file and decrypt, modify
5-
* and re-encrypt it to create an encryption-at-rest solution as discussed in
6-
* https://pdfrest.com/solutions/create-secure-document-workflows-with-pdf-password-protection/
7-
* We will be running the document through /decrypted-pdf to open the document
8-
* to modification, running the decrypted result through /pdf-with-added-image,
9-
* and then sending the output with the new image through /encrypted-pdf to
10-
* lock it up again.
11-
*/
12-
13-
var apiKey = "xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; // Your API key here
14-
15-
using (var httpClient = new HttpClient { BaseAddress = new Uri("https://api.pdfrest.com") })
23+
namespace Samples.ComplexFlowExamples
1624
{
17-
// Begin decryption
18-
using var decryptRequest = new HttpRequestMessage(HttpMethod.Post, "decrypted-pdf");
19-
20-
decryptRequest.Headers.TryAddWithoutValidation("Api-Key", apiKey);
21-
decryptRequest.Headers.Accept.Add(new("application/json"));
22-
var decryptMultipartContent = new MultipartFormDataContent();
23-
24-
var byteArray = File.ReadAllBytes("/path/to/file.pdf");
25-
var byteAryContent = new ByteArrayContent(byteArray);
26-
decryptMultipartContent.Add(byteAryContent, "file", "file.pdf");
27-
byteAryContent.Headers.TryAddWithoutValidation("Content-Type", "application/pdf");
28-
29-
var byteArrayOption = new ByteArrayContent(Encoding.UTF8.GetBytes("password"));
30-
decryptMultipartContent.Add(byteArrayOption, "current_open_password");
31-
32-
33-
decryptRequest.Content = decryptMultipartContent;
34-
var decryptResponse = await httpClient.SendAsync(decryptRequest);
35-
36-
var decryptResult = await decryptResponse.Content.ReadAsStringAsync();
37-
38-
Console.WriteLine("Decrypt response received.");
39-
Console.WriteLine(decryptResult);
40-
41-
dynamic decryptJson = JObject.Parse(decryptResult);
42-
string decryptID = decryptJson.outputId;
43-
44-
// Begin add image
45-
using var addImageRequest = new HttpRequestMessage(HttpMethod.Post, "pdf-with-added-image");
46-
47-
addImageRequest.Headers.TryAddWithoutValidation("Api-Key", apiKey);
48-
addImageRequest.Headers.Accept.Add(new("application/json"));
49-
var addImageMultipartContent = new MultipartFormDataContent();
50-
51-
var addImageId = new ByteArrayContent(Encoding.UTF8.GetBytes(decryptID));
52-
addImageMultipartContent.Add(addImageId, "id");
53-
54-
var addImageImage = File.ReadAllBytes("/path/to/file.png");
55-
var imageContent = new ByteArrayContent(addImageImage);
56-
addImageMultipartContent.Add(imageContent, "image_file", "file_name.png");
57-
imageContent.Headers.TryAddWithoutValidation("Content-Type", "image/png");
58-
59-
var addImagePage = new ByteArrayContent(Encoding.UTF8.GetBytes("1"));
60-
addImageMultipartContent.Add(addImagePage, "page");
61-
62-
var addImageX = new ByteArrayContent(Encoding.UTF8.GetBytes("0"));
63-
addImageMultipartContent.Add(addImageX, "x");
64-
var addImageY = new ByteArrayContent(Encoding.UTF8.GetBytes("0"));
65-
addImageMultipartContent.Add(addImageY, "y");
66-
67-
addImageRequest.Content = addImageMultipartContent;
68-
var addImageResponse = await httpClient.SendAsync(addImageRequest);
69-
70-
using var addImageResult = await addImageResponse.Content.ReadAsStringAsync();
71-
72-
Console.WriteLine("Add image response received.");
73-
Console.WriteLine(addImageResult);
74-
75-
dynamic addImageJson = JObject.Parse(addImageResult);
76-
string addImageID = addImageJson.outputId;
77-
78-
// Begin re-encryption
79-
var encryptRequest = new HttpRequestMessage(HttpMethod.Post, "encrypted-pdf");
80-
81-
encryptRequest.Headers.TryAddWithoutValidation("Api-Key", apiKey);
82-
encryptRequest.Headers.Accept.Add(new("application/json"));
83-
var multipartContent = new MultipartFormDataContent();
84-
85-
var encryptID = new ByteArrayContent(Encoding.UTF8.GetBytes(addImageID));
86-
multipartContent.Add(encryptID, "id");
87-
88-
var encryptPassword = new ByteArrayContent(Encoding.UTF8.GetBytes("password"));
89-
multipartContent.Add(encryptPassword, "new_open_password");
90-
91-
92-
encryptRequest.Content = multipartContent;
93-
var response = await httpClient.SendAsync(encryptRequest);
94-
95-
var apiResult = await response.Content.ReadAsStringAsync();
96-
97-
Console.WriteLine("Encrypt response received.");
98-
Console.WriteLine(apiResult);
25+
public static class DecryptAddReencrypt
26+
{
27+
public static async Task Execute(string[] args)
28+
{
29+
if (args == null || args.Length < 2)
30+
{
31+
Console.Error.WriteLine("decrypt-add-reencrypt requires <pdf> <image> [password]");
32+
Environment.Exit(1);
33+
return;
34+
}
35+
var pdfPath = args[0];
36+
var imgPath = args[1];
37+
var pwd = args.Length > 2 ? args[2] : "password";
38+
if (!File.Exists(pdfPath) || !File.Exists(imgPath))
39+
{
40+
Console.Error.WriteLine("One or more input files not found.");
41+
Environment.Exit(1);
42+
return;
43+
}
44+
var apiKey = Environment.GetEnvironmentVariable("PDFREST_API_KEY");
45+
if (string.IsNullOrWhiteSpace(apiKey))
46+
{
47+
Console.Error.WriteLine("Missing required environment variable: PDFREST_API_KEY");
48+
Environment.Exit(1);
49+
return;
50+
}
51+
var baseUrl = Environment.GetEnvironmentVariable("PDFREST_URL") ?? "https://api.pdfrest.com";
52+
53+
using (var httpClient = new HttpClient { BaseAddress = new Uri(baseUrl) })
54+
{
55+
// Decrypt
56+
using var decryptRequest = new HttpRequestMessage(HttpMethod.Post, "decrypted-pdf");
57+
decryptRequest.Headers.TryAddWithoutValidation("Api-Key", apiKey);
58+
decryptRequest.Headers.Accept.Add(new("application/json"));
59+
var decryptMultipartContent = new MultipartFormDataContent();
60+
var byteArray = File.ReadAllBytes(pdfPath);
61+
var byteAryContent = new ByteArrayContent(byteArray);
62+
decryptMultipartContent.Add(byteAryContent, "file", Path.GetFileName(pdfPath));
63+
byteAryContent.Headers.TryAddWithoutValidation("Content-Type", "application/octet-stream");
64+
var byteArrayOption = new ByteArrayContent(Encoding.UTF8.GetBytes(pwd));
65+
decryptMultipartContent.Add(byteArrayOption, "current_open_password");
66+
decryptRequest.Content = decryptMultipartContent;
67+
var decryptResponse = await httpClient.SendAsync(decryptRequest);
68+
var decryptResult = await decryptResponse.Content.ReadAsStringAsync();
69+
Console.WriteLine("Decrypt response received.");
70+
Console.WriteLine(decryptResult);
71+
dynamic decryptJson = JObject.Parse(decryptResult);
72+
string decryptID = decryptJson.outputId;
73+
74+
// Add image
75+
using var addImageRequest = new HttpRequestMessage(HttpMethod.Post, "pdf-with-added-image");
76+
addImageRequest.Headers.TryAddWithoutValidation("Api-Key", apiKey);
77+
addImageRequest.Headers.Accept.Add(new("application/json"));
78+
var addImageMultipartContent = new MultipartFormDataContent();
79+
var addImageId = new ByteArrayContent(Encoding.UTF8.GetBytes(decryptID));
80+
addImageMultipartContent.Add(addImageId, "id");
81+
var addImageImage = File.ReadAllBytes(imgPath);
82+
var imageContent = new ByteArrayContent(addImageImage);
83+
addImageMultipartContent.Add(imageContent, "image_file", Path.GetFileName(imgPath));
84+
imageContent.Headers.TryAddWithoutValidation("Content-Type", "application/octet-stream");
85+
var addImagePage = new ByteArrayContent(Encoding.UTF8.GetBytes("1"));
86+
addImageMultipartContent.Add(addImagePage, "page");
87+
var addImageX = new ByteArrayContent(Encoding.UTF8.GetBytes("0"));
88+
addImageMultipartContent.Add(addImageX, "x");
89+
var addImageY = new ByteArrayContent(Encoding.UTF8.GetBytes("0"));
90+
addImageMultipartContent.Add(addImageY, "y");
91+
addImageRequest.Content = addImageMultipartContent;
92+
var addImageResponse = await httpClient.SendAsync(addImageRequest);
93+
var addImageResult = await addImageResponse.Content.ReadAsStringAsync();
94+
Console.WriteLine("Add image response received.");
95+
Console.WriteLine(addImageResult);
96+
dynamic addImageJson = JObject.Parse(addImageResult);
97+
string addImageID = addImageJson.outputId;
98+
99+
// Re-encrypt
100+
using var encryptRequest = new HttpRequestMessage(HttpMethod.Post, "encrypted-pdf");
101+
encryptRequest.Headers.TryAddWithoutValidation("Api-Key", apiKey);
102+
encryptRequest.Headers.Accept.Add(new("application/json"));
103+
var multipartContent = new MultipartFormDataContent();
104+
var encryptID = new ByteArrayContent(Encoding.UTF8.GetBytes(addImageID));
105+
multipartContent.Add(encryptID, "id");
106+
var encryptPassword = new ByteArrayContent(Encoding.UTF8.GetBytes(pwd));
107+
multipartContent.Add(encryptPassword, "new_open_password");
108+
encryptRequest.Content = multipartContent;
109+
var response = await httpClient.SendAsync(encryptRequest);
110+
var apiResult = await response.Content.ReadAsStringAsync();
111+
Console.WriteLine("Encrypt response received.");
112+
Console.WriteLine(apiResult);
113+
}
114+
}
115+
}
99116
}

0 commit comments

Comments
 (0)