Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CPlusPlus/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# pdfRest configuration for C++ samples
# Copy to .env and set your values. Do not commit .env.

# Required: API key from https://pdfrest.com/getstarted/
PDFREST_API_KEY=your_api_key_here

# Optional: region override for EU/GDPR routing
# PDFREST_URL=https://eu-api.pdfrest.com

# Optional: immediate deletion of uploaded/generated files when supported
# PDFREST_DELETE_SENSITIVE_FILES=true
61 changes: 61 additions & 0 deletions CPlusPlus/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Created by https://www.toptal.com/developers/gitignore/api/c++,cmake
# Edit at https://www.toptal.com/developers/gitignore?templates=c++,cmake

### C++ ###
# Prerequisites
*.d

# Compiled Object files
*.slo
*.lo
*.o
*.obj

# Precompiled Headers
*.gch
*.pch

# Compiled Dynamic libraries
*.so
*.dylib
*.dll

# Fortran module files
*.mod
*.smod

# Compiled Static libraries
*.lai
*.la
*.a
*.lib

# Executables
*.exe
*.out
*.app

### CMake ###
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps

### CMake Patch ###
CMakeUserPresets.json

# External projects
*-prefix/

# End of https://www.toptal.com/developers/gitignore/api/c++,cmake

build

cmake-*
72 changes: 72 additions & 0 deletions CPlusPlus/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Repository Guidelines

## Project Structure & Module Organization
- `CMakeLists.txt`: CMake config (C++20 target).
- `main.cpp`: entry point and sample scaffold.
- `Endpoint Examples/JSON Payload/markdown.cpp`: JSON two‑step upload → markdown.
- `Endpoint Examples/JSON Payload/rasterized_pdf.cpp`: JSON two‑step upload → rasterized-pdf.
- `Endpoint Examples/Multipart Payload/markdown.cpp`: Multipart markdown (file + options).
- `Endpoint Examples/Multipart Payload/rasterized_pdf.cpp`: Multipart rasterized-pdf (file only).
- `Complex Flow Examples/merge_different_file_types.cpp`: Convert different types to PDF, then merge.
- `.env.example` → copy to `.env` (loaded automatically).
- `build/` or `cmake-build-*`: local build output (untracked).
- New samples: mirror repo layout (`Endpoint Examples/JSON Payload/`, `Endpoint Examples/Multipart Payload/`). Name files after endpoints.

## Build, Test, and Development Commands
- Install deps via vcpkg (pick one HTTP client):
- CPR: `vcpkg install cpr nlohmann-json`
- cpp-httplib: `vcpkg install cpp-httplib[openssl] nlohmann-json`
- Configure with vcpkg toolchain:
- macOS/Linux: `cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Debug`
- Windows (PowerShell): `cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -DCMAKE_BUILD_TYPE=Debug`
- Build: `cmake --build build -j` (Release: set at configure)
- Run: `./build/CPlusPlus` (Windows: `build\CPlusPlus.exe`)
- Tests (if added): `ctest --test-dir build -j`

## Coding Style & Naming Conventions
- C++20; 4‑space indentation; consistent brace style.
- Files: `snake_case.cpp/.h`; types: `PascalCase`; funcs/vars: `snake_case`; constants/macros: `UPPER_SNAKE_CASE`; namespace: `pdfrest`.
- Use `clang-format` (LLVM/Google) and keep functions small/clear.
- HTTP/JSON via vcpkg: prefer `cpr` (batteries‑included) or `cpp-httplib`[openssl]; JSON via `nlohmann::json`.

### CMake (vcpkg) example
```
find_package(nlohmann_json CONFIG REQUIRED)
# choose one client
find_package(cpr CONFIG REQUIRED) # or: find_package(httplib CONFIG REQUIRED)
target_link_libraries(CPlusPlus PRIVATE nlohmann_json::nlohmann_json cpr::cpr)
```

## Testing Guidelines
- None present. If adding tests, use GoogleTest via vcpkg: `vcpkg install gtest` and wire CTest (`enable_testing(); add_test(...)`).
- Put tests in `tests/` and name after samples (e.g., `markdown_test.cpp`). Failures must return non‑zero.

## Commit & Pull Request Guidelines
- Commits: concise, imperative; prefixes welcome: `feat(sample)`, `fix(sample)`, `chore(build)`, `docs(cpp)`.
- PRs: include purpose, endpoints touched, build/run example (with toolchain flag), sample output/artifact, and linked issues. Avoid unrelated changes.

## Security & Configuration Tips
- Do not commit secrets. Read `PDFREST_API_KEY` from environment.
- Region/GDPR: `PDFREST_URL=https://eu-api.pdfrest.com` (default `https://api.pdfrest.com`).
- Optional cleanup: respect `PDFREST_DELETE_SENSITIVE_FILES=true` when implemented.
- Respect proxies via `HTTPS_PROXY`/`HTTP_PROXY`. Never print API keys.

Note on `.env` loading
- The C++ samples auto-load `.env` from this folder or the parent folder using a small manual parser — no extra packages required.

## Sample Header Template
Place this at the top of each sample (before includes):
```
/*
* What this sample does:
* - Brief purpose and request style
*
* Setup (environment):
* - Set PDFREST_API_KEY=your_api_key_here
* - Optional: set PDFREST_URL (EU/GDPR: https://eu-api.pdfrest.com)
* More info: https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
*
* Usage: ./markdown_json <input.pdf>
* Output: JSON to stdout; non‑2xx exits with concise error.
*/
```
16 changes: 16 additions & 0 deletions CPlusPlus/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 4.0)
project(CPlusPlus)

set(CMAKE_CXX_STANDARD 20)

# Place all executables in the top-level build directory for convenience
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
foreach(OUTPUTCONFIG DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR})
endforeach()

# Define sample targets in subdirectories
add_subdirectory("Endpoint Examples/JSON Payload")
add_subdirectory("Endpoint Examples/Multipart Payload")
add_subdirectory("Complex Flow Examples")
10 changes: 10 additions & 0 deletions CPlusPlus/Complex Flow Examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.16)

find_package(cpr CONFIG QUIET)
find_package(nlohmann_json CONFIG QUIET)

if (cpr_FOUND AND nlohmann_json_FOUND)
add_executable(merge_different_file_types merge_different_file_types.cpp)
target_link_libraries(merge_different_file_types PRIVATE cpr::cpr nlohmann_json::nlohmann_json)
target_compile_features(merge_different_file_types PRIVATE cxx_std_20)
endif()
130 changes: 130 additions & 0 deletions CPlusPlus/Complex Flow Examples/merge_different_file_types.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* What this sample does:
* - Converts two different file types to PDF via multipart (/pdf), then merges them via /merged-pdf.
*
* Setup (environment):
* - Copy .env.example to .env
* - Set PDFREST_API_KEY=your_api_key_here
* - Optional: set PDFREST_URL to override the API region. For EU/GDPR compliance and proximity, use:
* PDFREST_URL=https://eu-api.pdfrest.com
* For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
*
* Usage:
* ./merge_different_file_types image.png slides.pptx
*
* Output:
* - Prints JSON responses for the two conversions and the final merge; non-2xx exits with concise error.
*/

#include <cpr/cpr.h>
#include <nlohmann/json.hpp>

#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>

namespace fs = std::filesystem;
using json = nlohmann::json;

static std::string rtrim_slashes(std::string s) {
while (!s.empty() && (s.back() == '/' || s.back() == '\\')) {
s.pop_back();
}
return s;
}

static void load_dotenv_if_present(const fs::path &p) {
std::ifstream f(p);
if (!f.is_open()) return;
std::string l;
while (std::getline(f, l)) {
if (l.empty() || l[0] == '#') continue;
auto pos = l.find('=');
if (pos == std::string::npos) continue;
std::string k = l.substr(0, pos);
std::string v = l.substr(pos + 1);
auto trim = [](std::string &s) {
size_t b = s.find_first_not_of(" \t\r\n");
size_t e = s.find_last_not_of(" \t\r\n");
if (b == std::string::npos) { s.clear(); return; }
s = s.substr(b, e - b + 1);
};
trim(k); trim(v);
if (k.empty()) continue;
if (!std::getenv(k.c_str())) {
#ifdef _WIN32
_putenv_s(k.c_str(), v.c_str());
#else
setenv(k.c_str(), v.c_str(), 0);
#endif
}
}
}

static void load_env() {
auto here = fs::current_path();
load_dotenv_if_present(here / ".env");
if (fs::exists(here.parent_path())) {
load_dotenv_if_present(here.parent_path() / ".env");
}
}

static std::string convert_to_pdf_id(const std::string &base,
const std::string &api_key,
const fs::path &input) {
cpr::Header hdr{{"Api-Key", api_key}, {"Accept", "application/json"}};
cpr::Multipart mp{{"file", cpr::File{input.string()}}};
auto res = cpr::Post(cpr::Url{base + "/pdf"}, hdr, mp);
if (res.error || res.status_code < 200 || res.status_code >= 300) {
throw std::runtime_error(
"Convert failed: " + std::to_string(res.status_code) + "\n" +
res.error.message + "\n" + res.text);
}
std::cout << res.text << "\n";
auto j = json::parse(res.text);
return j.at("outputId").get<std::string>();
}

int main(int argc, char **argv) {
load_env();
if (argc < 3) {
std::cerr << "Usage: merge_different_file_types <imageFile> <pptFile>\n";
return 1;
}
fs::path img = argv[1], ppt = argv[2];
if (!fs::exists(img) || !fs::exists(ppt)) {
std::cerr << "One or more input files not found.\n";
return 1;
}
const char *key = getenv("PDFREST_API_KEY");
if (!key || !*key) {
std::cerr << "Missing PDFREST_API_KEY\n";
return 1;
}
std::string base = getenv("PDFREST_URL") ? getenv("PDFREST_URL") : "https://api.pdfrest.com";
base = rtrim_slashes(base);

try {
std::string img_id = convert_to_pdf_id(base, key, img);
std::string ppt_id = convert_to_pdf_id(base, key, ppt);

cpr::Header hdr{{"Api-Key", key}, {"Accept", "application/json"}};
cpr::Multipart mp{
{"id[]", img_id}, {"type[]", "id"}, {"pages[]", "all"},
{"id[]", ppt_id}, {"type[]", "id"}, {"pages[]", "all"}
};
auto merge = cpr::Post(cpr::Url{base + "/merged-pdf"}, hdr, mp);
if (merge.error || merge.status_code < 200 || merge.status_code >= 300) {
std::cerr << "Merge failed (" << merge.status_code << ")\n"
<< merge.error.message << "\n" << merge.text << "\n";
return 1;
}
std::cout << merge.text << "\n";
} catch (const std::exception &e) {
std::cerr << e.what() << "\n";
return 1;
}
return 0;
}
14 changes: 14 additions & 0 deletions CPlusPlus/Endpoint Examples/JSON Payload/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.16)

find_package(cpr CONFIG QUIET)
find_package(nlohmann_json CONFIG QUIET)

if (cpr_FOUND AND nlohmann_json_FOUND)
add_executable(markdown_json markdown.cpp)
target_link_libraries(markdown_json PRIVATE cpr::cpr nlohmann_json::nlohmann_json)
target_compile_features(markdown_json PRIVATE cxx_std_20)

add_executable(rasterized_pdf_json rasterized_pdf.cpp)
target_link_libraries(rasterized_pdf_json PRIVATE cpr::cpr nlohmann_json::nlohmann_json)
target_compile_features(rasterized_pdf_json PRIVATE cxx_std_20)
endif()
Loading