Skip to content

Commit 98afe07

Browse files
Merge pull request #112 from datalogics-kam/pdfcloud-5200-cplusplus-samples
PDFCLOUD-5200 Add C++ samples
2 parents b9a138d + 816a512 commit 98afe07

13 files changed

Lines changed: 872 additions & 0 deletions

File tree

CPlusPlus/.env.example

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# pdfRest configuration for C++ samples
2+
# Copy to .env and set your values. Do not commit .env.
3+
4+
# Required: API key from https://pdfrest.com/getstarted/
5+
PDFREST_API_KEY=your_api_key_here
6+
7+
# Optional: region override for EU/GDPR routing
8+
# PDFREST_URL=https://eu-api.pdfrest.com
9+
10+
# Optional: immediate deletion of uploaded/generated files when supported
11+
# PDFREST_DELETE_SENSITIVE_FILES=true

CPlusPlus/.gitignore

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Created by https://www.toptal.com/developers/gitignore/api/c++,cmake
2+
# Edit at https://www.toptal.com/developers/gitignore?templates=c++,cmake
3+
4+
### C++ ###
5+
# Prerequisites
6+
*.d
7+
8+
# Compiled Object files
9+
*.slo
10+
*.lo
11+
*.o
12+
*.obj
13+
14+
# Precompiled Headers
15+
*.gch
16+
*.pch
17+
18+
# Compiled Dynamic libraries
19+
*.so
20+
*.dylib
21+
*.dll
22+
23+
# Fortran module files
24+
*.mod
25+
*.smod
26+
27+
# Compiled Static libraries
28+
*.lai
29+
*.la
30+
*.a
31+
*.lib
32+
33+
# Executables
34+
*.exe
35+
*.out
36+
*.app
37+
38+
### CMake ###
39+
CMakeLists.txt.user
40+
CMakeCache.txt
41+
CMakeFiles
42+
CMakeScripts
43+
Testing
44+
Makefile
45+
cmake_install.cmake
46+
install_manifest.txt
47+
compile_commands.json
48+
CTestTestfile.cmake
49+
_deps
50+
51+
### CMake Patch ###
52+
CMakeUserPresets.json
53+
54+
# External projects
55+
*-prefix/
56+
57+
# End of https://www.toptal.com/developers/gitignore/api/c++,cmake
58+
59+
build
60+
61+
cmake-*

CPlusPlus/AGENTS.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Repository Guidelines
2+
3+
## Project Structure & Module Organization
4+
- `CMakeLists.txt`: CMake config (C++20 target).
5+
- `main.cpp`: entry point and sample scaffold.
6+
- `Endpoint Examples/JSON Payload/markdown.cpp`: JSON two‑step upload → markdown.
7+
- `Endpoint Examples/JSON Payload/rasterized_pdf.cpp`: JSON two‑step upload → rasterized-pdf.
8+
- `Endpoint Examples/Multipart Payload/markdown.cpp`: Multipart markdown (file + options).
9+
- `Endpoint Examples/Multipart Payload/rasterized_pdf.cpp`: Multipart rasterized-pdf (file only).
10+
- `Complex Flow Examples/merge_different_file_types.cpp`: Convert different types to PDF, then merge.
11+
- `.env.example` → copy to `.env` (loaded automatically).
12+
- `build/` or `cmake-build-*`: local build output (untracked).
13+
- New samples: mirror repo layout (`Endpoint Examples/JSON Payload/`, `Endpoint Examples/Multipart Payload/`). Name files after endpoints.
14+
15+
## Build, Test, and Development Commands
16+
- Install deps via vcpkg (pick one HTTP client):
17+
- CPR: `vcpkg install cpr nlohmann-json`
18+
- cpp-httplib: `vcpkg install cpp-httplib[openssl] nlohmann-json`
19+
- Configure with vcpkg toolchain:
20+
- macOS/Linux: `cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Debug`
21+
- Windows (PowerShell): `cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -DCMAKE_BUILD_TYPE=Debug`
22+
- Build: `cmake --build build -j` (Release: set at configure)
23+
- Run: `./build/CPlusPlus` (Windows: `build\CPlusPlus.exe`)
24+
- Tests (if added): `ctest --test-dir build -j`
25+
26+
## Coding Style & Naming Conventions
27+
- C++20; 4‑space indentation; consistent brace style.
28+
- Files: `snake_case.cpp/.h`; types: `PascalCase`; funcs/vars: `snake_case`; constants/macros: `UPPER_SNAKE_CASE`; namespace: `pdfrest`.
29+
- Use `clang-format` (LLVM/Google) and keep functions small/clear.
30+
- HTTP/JSON via vcpkg: prefer `cpr` (batteries‑included) or `cpp-httplib`[openssl]; JSON via `nlohmann::json`.
31+
32+
### CMake (vcpkg) example
33+
```
34+
find_package(nlohmann_json CONFIG REQUIRED)
35+
# choose one client
36+
find_package(cpr CONFIG REQUIRED) # or: find_package(httplib CONFIG REQUIRED)
37+
target_link_libraries(CPlusPlus PRIVATE nlohmann_json::nlohmann_json cpr::cpr)
38+
```
39+
40+
## Testing Guidelines
41+
- None present. If adding tests, use GoogleTest via vcpkg: `vcpkg install gtest` and wire CTest (`enable_testing(); add_test(...)`).
42+
- Put tests in `tests/` and name after samples (e.g., `markdown_test.cpp`). Failures must return non‑zero.
43+
44+
## Commit & Pull Request Guidelines
45+
- Commits: concise, imperative; prefixes welcome: `feat(sample)`, `fix(sample)`, `chore(build)`, `docs(cpp)`.
46+
- PRs: include purpose, endpoints touched, build/run example (with toolchain flag), sample output/artifact, and linked issues. Avoid unrelated changes.
47+
48+
## Security & Configuration Tips
49+
- Do not commit secrets. Read `PDFREST_API_KEY` from environment.
50+
- Region/GDPR: `PDFREST_URL=https://eu-api.pdfrest.com` (default `https://api.pdfrest.com`).
51+
- Optional cleanup: respect `PDFREST_DELETE_SENSITIVE_FILES=true` when implemented.
52+
- Respect proxies via `HTTPS_PROXY`/`HTTP_PROXY`. Never print API keys.
53+
54+
Note on `.env` loading
55+
- The C++ samples auto-load `.env` from this folder or the parent folder using a small manual parser — no extra packages required.
56+
57+
## Sample Header Template
58+
Place this at the top of each sample (before includes):
59+
```
60+
/*
61+
* What this sample does:
62+
* - Brief purpose and request style
63+
*
64+
* Setup (environment):
65+
* - Set PDFREST_API_KEY=your_api_key_here
66+
* - Optional: set PDFREST_URL (EU/GDPR: https://eu-api.pdfrest.com)
67+
* More info: https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
68+
*
69+
* Usage: ./markdown_json <input.pdf>
70+
* Output: JSON to stdout; non‑2xx exits with concise error.
71+
*/
72+
```

CPlusPlus/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
cmake_minimum_required(VERSION 4.0)
2+
project(CPlusPlus)
3+
4+
set(CMAKE_CXX_STANDARD 20)
5+
6+
# Place all executables in the top-level build directory for convenience
7+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
8+
foreach(OUTPUTCONFIG DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
9+
string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER)
10+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR})
11+
endforeach()
12+
13+
# Define sample targets in subdirectories
14+
add_subdirectory("Endpoint Examples/JSON Payload")
15+
add_subdirectory("Endpoint Examples/Multipart Payload")
16+
add_subdirectory("Complex Flow Examples")
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
3+
find_package(cpr CONFIG QUIET)
4+
find_package(nlohmann_json CONFIG QUIET)
5+
6+
if (cpr_FOUND AND nlohmann_json_FOUND)
7+
add_executable(merge_different_file_types merge_different_file_types.cpp)
8+
target_link_libraries(merge_different_file_types PRIVATE cpr::cpr nlohmann_json::nlohmann_json)
9+
target_compile_features(merge_different_file_types PRIVATE cxx_std_20)
10+
endif()
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* What this sample does:
3+
* - Converts two different file types to PDF via multipart (/pdf), then merges them via /merged-pdf.
4+
*
5+
* Setup (environment):
6+
* - Copy .env.example to .env
7+
* - Set PDFREST_API_KEY=your_api_key_here
8+
* - Optional: set PDFREST_URL to override the API region. For EU/GDPR compliance and proximity, use:
9+
* PDFREST_URL=https://eu-api.pdfrest.com
10+
* For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
11+
*
12+
* Usage:
13+
* ./merge_different_file_types image.png slides.pptx
14+
*
15+
* Output:
16+
* - Prints JSON responses for the two conversions and the final merge; non-2xx exits with concise error.
17+
*/
18+
19+
#include <cpr/cpr.h>
20+
#include <nlohmann/json.hpp>
21+
22+
#include <cstdlib>
23+
#include <filesystem>
24+
#include <fstream>
25+
#include <iostream>
26+
#include <string>
27+
28+
namespace fs = std::filesystem;
29+
using json = nlohmann::json;
30+
31+
static std::string rtrim_slashes(std::string s) {
32+
while (!s.empty() && (s.back() == '/' || s.back() == '\\')) {
33+
s.pop_back();
34+
}
35+
return s;
36+
}
37+
38+
static void load_dotenv_if_present(const fs::path &p) {
39+
std::ifstream f(p);
40+
if (!f.is_open()) return;
41+
std::string l;
42+
while (std::getline(f, l)) {
43+
if (l.empty() || l[0] == '#') continue;
44+
auto pos = l.find('=');
45+
if (pos == std::string::npos) continue;
46+
std::string k = l.substr(0, pos);
47+
std::string v = l.substr(pos + 1);
48+
auto trim = [](std::string &s) {
49+
size_t b = s.find_first_not_of(" \t\r\n");
50+
size_t e = s.find_last_not_of(" \t\r\n");
51+
if (b == std::string::npos) { s.clear(); return; }
52+
s = s.substr(b, e - b + 1);
53+
};
54+
trim(k); trim(v);
55+
if (k.empty()) continue;
56+
if (!std::getenv(k.c_str())) {
57+
#ifdef _WIN32
58+
_putenv_s(k.c_str(), v.c_str());
59+
#else
60+
setenv(k.c_str(), v.c_str(), 0);
61+
#endif
62+
}
63+
}
64+
}
65+
66+
static void load_env() {
67+
auto here = fs::current_path();
68+
load_dotenv_if_present(here / ".env");
69+
if (fs::exists(here.parent_path())) {
70+
load_dotenv_if_present(here.parent_path() / ".env");
71+
}
72+
}
73+
74+
static std::string convert_to_pdf_id(const std::string &base,
75+
const std::string &api_key,
76+
const fs::path &input) {
77+
cpr::Header hdr{{"Api-Key", api_key}, {"Accept", "application/json"}};
78+
cpr::Multipart mp{{"file", cpr::File{input.string()}}};
79+
auto res = cpr::Post(cpr::Url{base + "/pdf"}, hdr, mp);
80+
if (res.error || res.status_code < 200 || res.status_code >= 300) {
81+
throw std::runtime_error(
82+
"Convert failed: " + std::to_string(res.status_code) + "\n" +
83+
res.error.message + "\n" + res.text);
84+
}
85+
std::cout << res.text << "\n";
86+
auto j = json::parse(res.text);
87+
return j.at("outputId").get<std::string>();
88+
}
89+
90+
int main(int argc, char **argv) {
91+
load_env();
92+
if (argc < 3) {
93+
std::cerr << "Usage: merge_different_file_types <imageFile> <pptFile>\n";
94+
return 1;
95+
}
96+
fs::path img = argv[1], ppt = argv[2];
97+
if (!fs::exists(img) || !fs::exists(ppt)) {
98+
std::cerr << "One or more input files not found.\n";
99+
return 1;
100+
}
101+
const char *key = getenv("PDFREST_API_KEY");
102+
if (!key || !*key) {
103+
std::cerr << "Missing PDFREST_API_KEY\n";
104+
return 1;
105+
}
106+
std::string base = getenv("PDFREST_URL") ? getenv("PDFREST_URL") : "https://api.pdfrest.com";
107+
base = rtrim_slashes(base);
108+
109+
try {
110+
std::string img_id = convert_to_pdf_id(base, key, img);
111+
std::string ppt_id = convert_to_pdf_id(base, key, ppt);
112+
113+
cpr::Header hdr{{"Api-Key", key}, {"Accept", "application/json"}};
114+
cpr::Multipart mp{
115+
{"id[]", img_id}, {"type[]", "id"}, {"pages[]", "all"},
116+
{"id[]", ppt_id}, {"type[]", "id"}, {"pages[]", "all"}
117+
};
118+
auto merge = cpr::Post(cpr::Url{base + "/merged-pdf"}, hdr, mp);
119+
if (merge.error || merge.status_code < 200 || merge.status_code >= 300) {
120+
std::cerr << "Merge failed (" << merge.status_code << ")\n"
121+
<< merge.error.message << "\n" << merge.text << "\n";
122+
return 1;
123+
}
124+
std::cout << merge.text << "\n";
125+
} catch (const std::exception &e) {
126+
std::cerr << e.what() << "\n";
127+
return 1;
128+
}
129+
return 0;
130+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
3+
find_package(cpr CONFIG QUIET)
4+
find_package(nlohmann_json CONFIG QUIET)
5+
6+
if (cpr_FOUND AND nlohmann_json_FOUND)
7+
add_executable(markdown_json markdown.cpp)
8+
target_link_libraries(markdown_json PRIVATE cpr::cpr nlohmann_json::nlohmann_json)
9+
target_compile_features(markdown_json PRIVATE cxx_std_20)
10+
11+
add_executable(rasterized_pdf_json rasterized_pdf.cpp)
12+
target_link_libraries(rasterized_pdf_json PRIVATE cpr::cpr nlohmann_json::nlohmann_json)
13+
target_compile_features(rasterized_pdf_json PRIVATE cxx_std_20)
14+
endif()

0 commit comments

Comments
 (0)