Convert HTML to PDF in C++

This guide shows how to use PDFCrowd's HTML to PDF API from C++ with libcurl. Your program sends an authenticated HTTP request containing a webpage URL, an HTML file, or an HTML string. PDFCrowd performs the conversion and returns the PDF in the response body.

Set up the example

To run the example, you need a C++17 compiler and the libcurl development files. Download convert.cpp, which includes the request, response callback, error checks, and automatic resource cleanup.

On a system with pkg-config configured for libcurl, compile it with:

c++ -std=c++17 -Wall -Wextra convert.cpp -o convert $(pkg-config --cflags --libs libcurl)

Set your API credentials in the API_USERNAME and API_KEY environment variables. Both can be demo for testing. For example, in a POSIX shell:

export API_USERNAME=demo
export API_KEY=demo

Convert a webpage to PDF

Run the downloaded program with a URL and an output filename:

./convert url https://example.com/ output.pdf

The request goes to https://api.pdfcrowd.com/convert/24.04/ over HTTPS, using your API username and API key for HTTP Basic authentication. The example sends multipart form fields; libcurl supplies the request's content type and boundary.

The following excerpt shows the request construction. In the complete program, curl and form are std::unique_ptr owners for the libcurl handle and multipart form. response is a std::string holding the returned bytes. The check() helper reports libcurl failures, add_text() adds a text field, and receive_bytes() collects the response:

check(curl_easy_setopt(curl.get(), CURLOPT_URL, "https://api.pdfcrowd.com/convert/24.04/"));
check(curl_easy_setopt(curl.get(), CURLOPT_USERNAME, username));
check(curl_easy_setopt(curl.get(), CURLOPT_PASSWORD, api_key));
check(curl_easy_setopt(curl.get(), CURLOPT_HTTPAUTH, CURLAUTH_BASIC));
check(curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, receive_bytes));
check(curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response));

add_text(form.get(), "input_format", "html");
add_text(form.get(), "output_format", "pdf");
add_text(form.get(), "content_viewport_width", "balanced");
add_text(form.get(), "page_size", "A4");
add_text(form.get(), "url", "https://example.com/");

check(curl_easy_setopt(curl.get(), CURLOPT_MIMEPOST, form.get()));
check(curl_easy_perform(curl.get()));

input_format and output_format select HTML to PDF conversion. The url field specifies the page to convert; it is separate from the API endpoint. content_viewport_width=balanced lets the converter choose a viewport width for the page. After checking for HTTP status 200, the program writes the binary response to output.pdf.

The webpage and its resources must be reachable by PDFCrowd. A URL on localhost refers to the conversion server, not the computer running this program.

Convert an HTML file or string

Use one input field per request: url, file, or text. The authentication and response handling stay the same.

Upload an HTML file

For a local file such as report.html, replace the url field with a file part:

auto *part = curl_mime_addpart(form.get());
if (!part)
    throw std::bad_alloc();
check(curl_mime_name(part, "file"));
check(curl_mime_filedata(part, "report.html"));

curl_mime_filedata() reads and uploads the local file. The downloadable program exposes this as:

./convert file report.html report.pdf

If the document depends on local images or stylesheets, package them with the HTML in an archive and upload the archive as file. Uploading the HTML alone does not include neighboring files. See the archive input example for supported formats and choosing the main HTML file.

Send an HTML string

For HTML produced by your application, replace the url field with text:

add_text(form.get(), "text", "<h1>Monthly report</h1><p>Revenue increased.</p>");

You can also try a short string with the downloaded program:

./convert text '<h1>Monthly report</h1><p>Revenue increased.</p>' report.pdf

Use UTF-8 for HTML text. Images and stylesheets in uploaded or string HTML can use absolute, publicly reachable URLs. To resolve relative URLs against a public location, include a <base href="https://example.com/assets/"> element in the HTML document's <head>.

Customize the PDF

Conversion options are additional form fields. For example, add these before sending the request to set the top and bottom margins and use print stylesheets:

add_text(form.get(), "margin_top", "20mm");
add_text(form.get(), "margin_bottom", "20mm");
add_text(form.get(), "use_print_media", "true");

The main example already sets page_size to A4; replace that value to change the page format. Use the HTTP parameter names and string values from the parameter reference. Add each option once to the form.

Handle errors

A completed HTTP transfer can still contain an API error. The program checks the HTTP status before opening the output file:

long status = 0;
check(curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &status));
if (status != 200) {
    std::cerr << "PDFCrowd returned HTTP " << status << ":\n" << response << '\n';
    return EXIT_FAILURE;
}

std::string retains the response's length, including any zero bytes in a PDF. The HTTP response documentation explains API errors.

The complete program catches exceptions from setup, requests, and file writes and returns a nonzero exit status on failure. Its resource owners release the form and libcurl handle when they leave scope. The response callback catches allocation failures internally so an exception cannot cross the C callback boundary.

The example uses a 30-second connection timeout and a 120-second total request timeout; adjust these for your application's expected conversion time. It buffers the response in memory. For large outputs, a streaming implementation can write to a temporary file and keep it only after the transfer and HTTP status checks succeed.