Getting Started
The following section will walk you through how to go from a simple HTML file to a fully featured invoice, making use of all the functionality the PDF Generator API provides.
The Basics of PDF Generation
Let's start with the basics. PDF Generation allows you to create a completely new PDF from a simple HTML document. You can make use of all the tools you'd usually use like:
- CSS for styling
- Images and fonts
- HTML forms
Our PDF Generation API will convert these to a PDF. Let's look at the most basic example. This will generate a PDF with the text "Hello World, I am red!" in large black letters. Don't worry about the color; we'll get to that later.
To run this, create an index.html
file containing the HTML in the same folder as your code. Run the code, and you should get
result.pdf
with your newly generated PDF.
HTML template section:
<h1>Hello World, I am red!</h1>
<p>And I am green.</p>
Command to generate a PDF file:
curl -X POST https://api.nutrient.io/build \
-H "Authorization: Bearer your_api_key_here" \
-o result.pdf \
--fail \
-F [email protected] \
-F instructions='{
"parts": [
{
"html": "index.html"
}
]
}'
curl -X POST https://api.nutrient.io/build ^
-H "Authorization: Bearer your_api_key_here" ^
-o result.pdf ^
--fail ^
-F [email protected] ^
-F instructions="{\"parts\": [{\"html\": \"index.html\"}]}"
package com.example.pspdfkit;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import org.json.JSONArray;
import org.json.JSONObject;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public final class PspdfkitApiExample {
public static void main(final String[] args) throws IOException {
final RequestBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(
"index.html",
"index.html",
RequestBody.create(
MediaType.parse("text/html"),
new File("index.html")
)
)
.addFormDataPart(
"instructions",
new JSONObject()
.put("parts", new JSONArray()
.put(new JSONObject()
.put("html", "index.html")
)
).toString()
)
.build();
final Request request = new Request.Builder()
.url("https://api.nutrient.io/build")
.method("POST", body)
.addHeader("Authorization", "Bearer your_api_key_here")
.build();
final OkHttpClient client = new OkHttpClient()
.newBuilder()
.build();
final Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
Files.copy(
response.body().byteStream(),
FileSystems.getDefault().getPath("result.pdf"),
StandardCopyOption.REPLACE_EXISTING
);
} else {
// Handle the error
throw new IOException(response.body().string());
}
}
}
using System;
using System.IO;
using System.Net;
using RestSharp;
namespace PspdfkitApiDemo
{
class Program
{
static void Main(string[] args)
{
var client = new RestClient("https://api.nutrient.io/build");
var request = new RestRequest(Method.POST)
.AddHeader("Authorization", "Bearer your_api_key_here")
.AddFile("index.html", "index.html")
.AddParameter("instructions", new JsonObject
{
["parts"] = new JsonArray
{
new JsonObject
{
["html"] = "index.html"
}
}
}.ToString());
request.AdvancedResponseWriter = (responseStream, response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (responseStream)
{
using var outputFileWriter = File.OpenWrite("result.pdf");
responseStream.CopyTo(outputFileWriter);
}
}
else
{
var responseStreamReader = new StreamReader(responseStream);
Console.Write(responseStreamReader.ReadToEnd());
}
};
client.Execute(request);
}
}
}
// This code requires Node.js. Do not run this code directly in a web browser.
const axios = require('axios')
const FormData = require('form-data')
const fs = require('fs')
const formData = new FormData()
formData.append('instructions', JSON.stringify({
parts: [
{
html: "index.html"
}
]
}))
formData.append('index.html', fs.createReadStream('index.html'))
;(async () => {
try {
const response = await axios.post('https://api.nutrient.io/build', formData, {
headers: formData.getHeaders({
'Authorization': 'Bearer your_api_key_here'
}),
responseType: "stream"
})
response.data.pipe(fs.createWriteStream("result.pdf"))
} catch (e) {
const errorString = await streamToString(e.response.data)
console.log(errorString)
}
})()
function streamToString(stream) {
const chunks = []
return new Promise((resolve, reject) => {
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)))
stream.on("error", (err) => reject(err))
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")))
})
}
import requests
import json
response = requests.request(
'POST',
'https://api.nutrient.io/build',
headers = {
'Authorization': 'Bearer your_api_key_here'
},
files = {
'index.html': open('index.html', 'rb')
},
data = {
'instructions': json.dumps({
'parts': [
{
'html': 'index.html'
}
]
})
},
stream = True
)
if response.ok:
with open('result.pdf', 'wb') as fd:
for chunk in response.iter_content(chunk_size=8096):
fd.write(chunk)
else:
print(response.text)
exit()
<?php
$FileHandle = fopen('result.pdf', 'w+');
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.nutrient.io/build',
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_POSTFIELDS => array(
'instructions' => '{
"parts": [
{
"html": "index.html"
}
]
}',
'index.html' => new CURLFILE('index.html')
),
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer your_api_key_here'
),
CURLOPT_FILE => $FileHandle,
));
$response = curl_exec($curl);
curl_close($curl);
fclose($FileHandle);
POST https://api.nutrient.io/build HTTP/1.1
Content-Type: multipart/form-data; boundary=--customboundary
Authorization: Bearer your_api_key_here
--customboundary
Content-Disposition: form-data; name="instructions"
Content-Type: application/json
{
"parts": [
{
"html": "index.html"
}
]
}
--customboundary
Content-Disposition: form-data; name="index.html"; filename="index.html"
Content-Type: text/html
(index.html data)
--customboundary--
Applying Basic Styling
Now you have your basic PDF, but in spite of what the text says, it's all black. Luckily, you can use inline CSS to style your text.
In this case, you'll make the heading red and the text below green. Update your
index.html
file, adding the inline styles, and run your code again.
HTML template section:
<h1 style="color: red;">Hello World, I am red!</h1>
<p style="color: green;">And I am green.</p>
Command to generate a PDF file:
curl -X POST https://api.nutrient.io/build \
-H "Authorization: Bearer your_api_key_here" \
-o result.pdf \
--fail \
-F [email protected] \
-F instructions='{
"parts": [
{
"html": "index.html"
}
]
}'
curl -X POST https://api.nutrient.io/build ^
-H "Authorization: Bearer your_api_key_here" ^
-o result.pdf ^
--fail ^
-F [email protected] ^
-F instructions="{\"parts\": [{\"html\": \"index.html\"}]}"
package com.example.pspdfkit;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import org.json.JSONArray;
import org.json.JSONObject;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public final class PspdfkitApiExample {
public static void main(final String[] args) throws IOException {
final RequestBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(
"index.html",
"index.html",
RequestBody.create(
MediaType.parse("text/html"),
new File("index.html")
)
)
.addFormDataPart(
"instructions",
new JSONObject()
.put("parts", new JSONArray()
.put(new JSONObject()
.put("html", "index.html")
)
).toString()
)
.build();
final Request request = new Request.Builder()
.url("https://api.nutrient.io/build")
.method("POST", body)
.addHeader("Authorization", "Bearer your_api_key_here")
.build();
final OkHttpClient client = new OkHttpClient()
.newBuilder()
.build();
final Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
Files.copy(
response.body().byteStream(),
FileSystems.getDefault().getPath("result.pdf"),
StandardCopyOption.REPLACE_EXISTING
);
} else {
// Handle the error
throw new IOException(response.body().string());
}
}
}
using System;
using System.IO;
using System.Net;
using RestSharp;
namespace PspdfkitApiDemo
{
class Program
{
static void Main(string[] args)
{
var client = new RestClient("https://api.nutrient.io/build");
var request = new RestRequest(Method.POST)
.AddHeader("Authorization", "Bearer your_api_key_here")
.AddFile("index.html", "index.html")
.AddParameter("instructions", new JsonObject
{
["parts"] = new JsonArray
{
new JsonObject
{
["html"] = "index.html"
}
}
}.ToString());
request.AdvancedResponseWriter = (responseStream, response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (responseStream)
{
using var outputFileWriter = File.OpenWrite("result.pdf");
responseStream.CopyTo(outputFileWriter);
}
}
else
{
var responseStreamReader = new StreamReader(responseStream);
Console.Write(responseStreamReader.ReadToEnd());
}
};
client.Execute(request);
}
}
}
// This code requires Node.js. Do not run this code directly in a web browser.
const axios = require('axios')
const FormData = require('form-data')
const fs = require('fs')
const formData = new FormData()
formData.append('instructions', JSON.stringify({
parts: [
{
html: "index.html"
}
]
}))
formData.append('index.html', fs.createReadStream('index.html'))
;(async () => {
try {
const response = await axios.post('https://api.nutrient.io/build', formData, {
headers: formData.getHeaders({
'Authorization': 'Bearer your_api_key_here'
}),
responseType: "stream"
})
response.data.pipe(fs.createWriteStream("result.pdf"))
} catch (e) {
const errorString = await streamToString(e.response.data)
console.log(errorString)
}
})()
function streamToString(stream) {
const chunks = []
return new Promise((resolve, reject) => {
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)))
stream.on("error", (err) => reject(err))
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")))
})
}
import requests
import json
response = requests.request(
'POST',
'https://api.nutrient.io/build',
headers = {
'Authorization': 'Bearer your_api_key_here'
},
files = {
'index.html': open('index.html', 'rb')
},
data = {
'instructions': json.dumps({
'parts': [
{
'html': 'index.html'
}
]
})
},
stream = True
)
if response.ok:
with open('result.pdf', 'wb') as fd:
for chunk in response.iter_content(chunk_size=8096):
fd.write(chunk)
else:
print(response.text)
exit()
<?php
$FileHandle = fopen('result.pdf', 'w+');
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.nutrient.io/build',
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_POSTFIELDS => array(
'instructions' => '{
"parts": [
{
"html": "index.html"
}
]
}',
'index.html' => new CURLFILE('index.html')
),
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer your_api_key_here'
),
CURLOPT_FILE => $FileHandle,
));
$response = curl_exec($curl);
curl_close($curl);
fclose($FileHandle);
POST https://api.nutrient.io/build HTTP/1.1
Content-Type: multipart/form-data; boundary=--customboundary
Authorization: Bearer your_api_key_here
--customboundary
Content-Disposition: form-data; name="instructions"
Content-Type: application/json
{
"parts": [
{
"html": "index.html"
}
]
}
--customboundary
Content-Disposition: form-data; name="index.html"; filename="index.html"
Content-Type: text/html
(index.html data)
--customboundary--
Introducing Assets
While inline styling works, more commonly, you'll want to have all your styles in a single CSS file you include. As luck would have it, our PDF Generation API supports this.
You can send as many additional assets with your request as you require. For now, you'll just add a CSS file to keep track of all your styles. Create
style.css
in the same folder as your HTML file and copy the content.
CSS file:
h1 {
color: red;
}
p {
color: green;
}
To make use of your new style.css
, you'll have to update your
index.html
file.
When updating your HTML file, make sure to refer to the CSS file with just its name. Nested paths aren't currently supported, and assets need to always be referred to without any path.
HTML template section:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>Hello World, I am red!</h1>
<p>And I am green.</p>
</body>
</html>
You'll also have to include your new CSS file in your request. It needs to be added both as an additional file to be sent with the request, and in the
assets
key of your HTML part.
Command to generate a PDF file:
curl -X POST https://api.nutrient.io/build \
-H "Authorization: Bearer your_api_key_here" \
-o result.pdf \
--fail \
-F [email protected] \
-F [email protected] \
-F instructions='{
"parts": [
{
"html": "index.html",
"assets": [
"style.css"
]
}
]
}'
curl -X POST https://api.nutrient.io/build ^
-H "Authorization: Bearer your_api_key_here" ^
-o result.pdf ^
--fail ^
-F [email protected] ^
-F [email protected] ^
-F instructions="{\"parts\": [{\"html\": \"index.html\", \"assets\": [\"style.css\"]}]}"
package com.example.pspdfkit;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import org.json.JSONArray;
import org.json.JSONObject;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public final class PspdfkitApiExample {
public static void main(final String[] args) throws IOException {
final RequestBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(
"index.html",
"index.html",
RequestBody.create(
MediaType.parse("text/html"),
new File("index.html")
)
)
.addFormDataPart(
"style.css",
"style.css",
RequestBody.create(
MediaType.parse("text/css"),
new File("style.css")
)
)
.addFormDataPart(
"instructions",
new JSONObject()
.put("parts", new JSONArray()
.put(new JSONObject()
.put("html", "index.html")
.put("assets", new JSONArray()
.put("style.css")
)
)
).toString()
)
.build();
final Request request = new Request.Builder()
.url("https://api.nutrient.io/build")
.method("POST", body)
.addHeader("Authorization", "Bearer your_api_key_here")
.build();
final OkHttpClient client = new OkHttpClient()
.newBuilder()
.build();
final Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
Files.copy(
response.body().byteStream(),
FileSystems.getDefault().getPath("result.pdf"),
StandardCopyOption.REPLACE_EXISTING
);
} else {
// Handle the error
throw new IOException(response.body().string());
}
}
}
using System;
using System.IO;
using System.Net;
using RestSharp;
namespace PspdfkitApiDemo
{
class Program
{
static void Main(string[] args)
{
var client = new RestClient("https://api.nutrient.io/build");
var request = new RestRequest(Method.POST)
.AddHeader("Authorization", "Bearer your_api_key_here")
.AddFile("index.html", "index.html")
.AddFile("style.css", "style.css")
.AddParameter("instructions", new JsonObject
{
["parts"] = new JsonArray
{
new JsonObject
{
["html"] = "index.html",
["assets"] = new JsonArray
{
"style.css"
}
}
}
}.ToString());
request.AdvancedResponseWriter = (responseStream, response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (responseStream)
{
using var outputFileWriter = File.OpenWrite("result.pdf");
responseStream.CopyTo(outputFileWriter);
}
}
else
{
var responseStreamReader = new StreamReader(responseStream);
Console.Write(responseStreamReader.ReadToEnd());
}
};
client.Execute(request);
}
}
}
// This code requires Node.js. Do not run this code directly in a web browser.
const axios = require('axios')
const FormData = require('form-data')
const fs = require('fs')
const formData = new FormData()
formData.append('instructions', JSON.stringify({
parts: [
{
html: "index.html",
assets: [
"style.css"
]
}
]
}))
formData.append('index.html', fs.createReadStream('index.html'))
formData.append('style.css', fs.createReadStream('style.css'))
;(async () => {
try {
const response = await axios.post('https://api.nutrient.io/build', formData, {
headers: formData.getHeaders({
'Authorization': 'Bearer your_api_key_here'
}),
responseType: "stream"
})
response.data.pipe(fs.createWriteStream("result.pdf"))
} catch (e) {
const errorString = await streamToString(e.response.data)
console.log(errorString)
}
})()
function streamToString(stream) {
const chunks = []
return new Promise((resolve, reject) => {
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)))
stream.on("error", (err) => reject(err))
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")))
})
}
import requests
import json
response = requests.request(
'POST',
'https://api.nutrient.io/build',
headers = {
'Authorization': 'Bearer your_api_key_here'
},
files = {
'index.html': open('index.html', 'rb'),
'style.css': open('style.css', 'rb')
},
data = {
'instructions': json.dumps({
'parts': [
{
'html': 'index.html',
'assets': [
'style.css'
]
}
]
})
},
stream = True
)
if response.ok:
with open('result.pdf', 'wb') as fd:
for chunk in response.iter_content(chunk_size=8096):
fd.write(chunk)
else:
print(response.text)
exit()
<?php
$FileHandle = fopen('result.pdf', 'w+');
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.nutrient.io/build',
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_POSTFIELDS => array(
'instructions' => '{
"parts": [
{
"html": "index.html",
"assets": [
"style.css"
]
}
]
}',
'index.html' => new CURLFILE('index.html'),
'style.css' => new CURLFILE('style.css')
),
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer your_api_key_here'
),
CURLOPT_FILE => $FileHandle,
));
$response = curl_exec($curl);
curl_close($curl);
fclose($FileHandle);
POST https://api.nutrient.io/build HTTP/1.1
Content-Type: multipart/form-data; boundary=--customboundary
Authorization: Bearer your_api_key_here
--customboundary
Content-Disposition: form-data; name="instructions"
Content-Type: application/json
{
"parts": [
{
"html": "index.html",
"assets": [
"style.css"
]
}
]
}
--customboundary
Content-Disposition: form-data; name="index.html"; filename="index.html"
Content-Type: text/html
(index.html data)
--customboundary
Content-Disposition: form-data; name="style.css"; filename="style.css"
Content-Type: text/css
(style.css data)
--customboundary--
Creating An Invoice
With the core building blocks (the HTML and external assets) out of the way, let's look at a real-world example: creating a PDF invoice. You'll start with the basic invoice and then add some advanced features on top.
Basics
CSS
First, you'll update your CSS. All the code is taken from the
"Invoice" example
you can download at the bottom, but the relevant parts are also duplicated here.
First, replace the contents of style.css
. Define some common styles used throughout the invoice, as well as the styles only applying to the header.
CSS file:
body {
font-size: 0.75rem;
font-weight: 400;
color: #000000;
margin: 0 auto;
position: relative;
}
h2 {
font-size: 1.25rem;
font-weight: 400;
}
h4 {
font-size: 1rem;
font-weight: 400;
}
.page {
margin-left: 5rem;
margin-right: 5rem;
}
.intro-table {
display: flex;
justify-content: space-between;
margin: 3rem 0 3rem 0;
border-top: 1px solid #000000;
border-bottom: 1px solid #000000;
}
.intro-form {
display: flex;
flex-direction: column;
border-right: 1px solid #000000;
width: 50%;
}
.intro-form:last-child {
border-right: none;
}
.intro-table-title {
font-size: 0.625rem;
margin: 0;
}
.intro-form-item {
padding: 1.25rem 1.5rem 1.25rem 1.5rem;
}
.intro-form-item:first-child {
padding-left: 0;
}
.intro-form-item:last-child {
padding-right: 0;
}
.intro-form-item-border {
padding: 1.25rem 0 0.75rem 1.5rem;
border-bottom: 1px solid #000000;
}
.intro-form-item-border:last-child {
border-bottom: none;
}
HTML
Next, update your index.html
file, adding the invoice numbers, as well as an address block, and some additional data, like date and type of payment.
As you can see, you're only using plain HTML here.
HTML template section:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="page" style="page-break-after: always">
<div>
<h2>Invoice #</h2>
</div>
<div class="intro-table">
<div class="intro-form intro-form-item">
<p class="intro-table-title">Billed To:</p>
<p>
Company Ltd.<br />
Address<br />
Country<br />
VAT ID: ATU12345678
</p>
</div>
<div class="intro-form">
<div class="intro-form-item-border">
<p class="intro-table-title">Payment Date:</p>
<p>November 22nd 2021</p>
</div>
<div class="intro-form-item-border">
<p class="intro-table-title">Payment Method:</p>
<p>Bank Transfer</p>
</div>
</div>
</div>
</div>
</body>
</html>
Code
If you rerun the generation with the new HTML and CSS, you'll see a nicely formatted invoice header in your PDF.
Command to generate a PDF file:
curl -X POST https://api.nutrient.io/build \
-H "Authorization: Bearer your_api_key_here" \
-o result.pdf \
--fail \
-F [email protected] \
-F [email protected] \
-F instructions='{
"parts": [
{
"html": "index.html",
"assets": [
"style.css"
]
}
]
}'
curl -X POST https://api.nutrient.io/build ^
-H "Authorization: Bearer your_api_key_here" ^
-o result.pdf ^
--fail ^
-F [email protected] ^
-F [email protected] ^
-F instructions="{\"parts\": [{\"html\": \"index.html\", \"assets\": [\"style.css\"]}]}"
package com.example.pspdfkit;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import org.json.JSONArray;
import org.json.JSONObject;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public final class PspdfkitApiExample {
public static void main(final String[] args) throws IOException {
final RequestBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(
"index.html",
"index.html",
RequestBody.create(
MediaType.parse("text/html"),
new File("index.html")
)
)
.addFormDataPart(
"style.css",
"style.css",
RequestBody.create(
MediaType.parse("text/css"),
new File("style.css")
)
)
.addFormDataPart(
"instructions",
new JSONObject()
.put("parts", new JSONArray()
.put(new JSONObject()
.put("html", "index.html")
.put("assets", new JSONArray()
.put("style.css")
)
)
).toString()
)
.build();
final Request request = new Request.Builder()
.url("https://api.nutrient.io/build")
.method("POST", body)
.addHeader("Authorization", "Bearer your_api_key_here")
.build();
final OkHttpClient client = new OkHttpClient()
.newBuilder()
.build();
final Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
Files.copy(
response.body().byteStream(),
FileSystems.getDefault().getPath("result.pdf"),
StandardCopyOption.REPLACE_EXISTING
);
} else {
// Handle the error
throw new IOException(response.body().string());
}
}
}
using System;
using System.IO;
using System.Net;
using RestSharp;
namespace PspdfkitApiDemo
{
class Program
{
static void Main(string[] args)
{
var client = new RestClient("https://api.nutrient.io/build");
var request = new RestRequest(Method.POST)
.AddHeader("Authorization", "Bearer your_api_key_here")
.AddFile("index.html", "index.html")
.AddFile("style.css", "style.css")
.AddParameter("instructions", new JsonObject
{
["parts"] = new JsonArray
{
new JsonObject
{
["html"] = "index.html",
["assets"] = new JsonArray
{
"style.css"
}
}
}
}.ToString());
request.AdvancedResponseWriter = (responseStream, response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (responseStream)
{
using var outputFileWriter = File.OpenWrite("result.pdf");
responseStream.CopyTo(outputFileWriter);
}
}
else
{
var responseStreamReader = new StreamReader(responseStream);
Console.Write(responseStreamReader.ReadToEnd());
}
};
client.Execute(request);
}
}
}
// This code requires Node.js. Do not run this code directly in a web browser.
const axios = require('axios')
const FormData = require('form-data')
const fs = require('fs')
const formData = new FormData()
formData.append('instructions', JSON.stringify({
parts: [
{
html: "index.html",
assets: [
"style.css"
]
}
]
}))
formData.append('index.html', fs.createReadStream('index.html'))
formData.append('style.css', fs.createReadStream('style.css'))
;(async () => {
try {
const response = await axios.post('https://api.nutrient.io/build', formData, {
headers: formData.getHeaders({
'Authorization': 'Bearer your_api_key_here'
}),
responseType: "stream"
})
response.data.pipe(fs.createWriteStream("result.pdf"))
} catch (e) {
const errorString = await streamToString(e.response.data)
console.log(errorString)
}
})()
function streamToString(stream) {
const chunks = []
return new Promise((resolve, reject) => {
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)))
stream.on("error", (err) => reject(err))
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")))
})
}
import requests
import json
response = requests.request(
'POST',
'https://api.nutrient.io/build',
headers = {
'Authorization': 'Bearer your_api_key_here'
},
files = {
'index.html': open('index.html', 'rb'),
'style.css': open('style.css', 'rb')
},
data = {
'instructions': json.dumps({
'parts': [
{
'html': 'index.html',
'assets': [
'style.css'
]
}
]
})
},
stream = True
)
if response.ok:
with open('result.pdf', 'wb') as fd:
for chunk in response.iter_content(chunk_size=8096):
fd.write(chunk)
else:
print(response.text)
exit()
<?php
$FileHandle = fopen('result.pdf', 'w+');
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.nutrient.io/build',
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_POSTFIELDS => array(
'instructions' => '{
"parts": [
{
"html": "index.html",
"assets": [
"style.css"
]
}
]
}',
'index.html' => new CURLFILE('index.html'),
'style.css' => new CURLFILE('style.css')
),
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer your_api_key_here'
),
CURLOPT_FILE => $FileHandle,
));
$response = curl_exec($curl);
curl_close($curl);
fclose($FileHandle);
POST https://api.nutrient.io/build HTTP/1.1
Content-Type: multipart/form-data; boundary=--customboundary
Authorization: Bearer your_api_key_here
--customboundary
Content-Disposition: form-data; name="instructions"
Content-Type: application/json
{
"parts": [
{
"html": "index.html",
"assets": [
"style.css"
]
}
]
}
--customboundary
Content-Disposition: form-data; name="index.html"; filename="index.html"
Content-Type: text/html
(index.html data)
--customboundary
Content-Disposition: form-data; name="style.css"; filename="style.css"
Content-Type: text/css
(style.css data)
--customboundary--
Custom Fonts
CSS
Now that you've got your basic header, you'll make things a bit nicer and add some custom fonts. Much like with the CSS file, all the fonts you use need to be placed in the same folder as your HTML file and then added to your code for making requests to Nutrient DWS API. (The example is already updated to show you how.)
One thing to consider is that, as discussed before, you should always refer to assets by their name, as subfolders aren't supported.
First, you have to add the @font-family
definitions to your CSS file. The example shows you the entire updated
style.css
file.
CSS file:
@font-face {
font-family: "Inter";
src: url("Inter-Regular.ttf") format("truetype");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "Inter";
src: url("Inter-Medium.ttf") format("truetype");
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: "Inter";
src: url("Inter-Bold.ttf") format("truetype");
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: "Space Mono";
src: url("SpaceMono-Regular.ttf") format("truetype");
font-weight: 400;
font-style: normal;
}
body {
font-size: 0.75rem;
font-family: "Inter", sans-serif;
font-weight: 400;
color: #000000;
margin: 0 auto;
position: relative;
}
h2 {
font-family: "Space Mono", monospace;
font-size: 1.25rem;
font-weight: 400;
}
h4 {
font-family: "Space Mono", monospace;
font-size: 1rem;
font-weight: 400;
}
.page {
margin-left: 5rem;
margin-right: 5rem;
}
.intro-table {
display: flex;
justify-content: space-between;
margin: 3rem 0 3rem 0;
border-top: 1px solid #000000;
border-bottom: 1px solid #000000;
}
.intro-form {
display: flex;
flex-direction: column;
border-right: 1px solid #000000;
width: 50%;
}
.intro-form:last-child {
border-right: none;
}
.intro-table-title {
font-size: 0.625rem;
margin: 0;
}
.intro-form-item {
padding: 1.25rem 1.5rem 1.25rem 1.5rem;
}
.intro-form-item:first-child {
padding-left: 0;
}
.intro-form-item:last-child {
padding-right: 0;
}
.intro-form-item-border {
padding: 1.25rem 0 0.75rem 1.5rem;
border-bottom: 1px solid #000000;
}
.intro-form-item-border:last-child {
border-bottom: none;
}
Code
Then you have to add the fonts to your code for making requests. You also have to add the font files in the same folder as your HTML file and CSS file. This way, they can be sent with the request. You can find the fonts as part of our invoice example.
Add all the font files to the same folder as your index.html
and style.css
files.
If you rerun the new code with the new CSS file, you should now see the fonts in the PDF replaced by your newly provided fonts, giving the invoice a cleaner look.
Command to generate a PDF file:
curl -X POST https://api.nutrient.io/build \
-H "Authorization: Bearer your_api_key_here" \
-o result.pdf \
--fail \
-F [email protected] \
-F [email protected] \
-F [email protected] \
-F [email protected] \
-F [email protected] \
-F [email protected] \
-F instructions='{
"parts": [
{
"html": "index.html",
"assets": [
"style.css",
"Inter-Regular.ttf",
"Inter-Medium.ttf",
"Inter-Bold.ttf",
"SpaceMono-Regular.ttf"
]
}
]
}'
curl -X POST https://api.nutrient.io/build ^
-H "Authorization: Bearer your_api_key_here" ^
-o result.pdf ^
--fail ^
-F [email protected] ^
-F [email protected] ^
-F [email protected] ^
-F [email protected] ^
-F [email protected] ^
-F [email protected] ^
-F instructions="{\"parts\": [{\"html\": \"index.html\", \"assets\": [\"style.css\", \"Inter-Regular.ttf\", \"Inter-Medium.ttf\", \"Inter-Bold.ttf\", \"SpaceMono-Regular.ttf\"]}]}"
package com.example.pspdfkit;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import org.json.JSONArray;
import org.json.JSONObject;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public final class PspdfkitApiExample {
public static void main(final String[] args) throws IOException {
final RequestBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(
"index.html",
"index.html",
RequestBody.create(
MediaType.parse("text/html"),
new File("index.html")
)
)
.addFormDataPart(
"style.css",
"style.css",
RequestBody.create(
MediaType.parse("text/css"),
new File("style.css")
)
)
.addFormDataPart(
"Inter-Regular.ttf",
"Inter-Regular.ttf",
RequestBody.create(
MediaType.parse("font/ttf"),
new File("Inter-Regular.ttf")
)
)
.addFormDataPart(
"Inter-Medium.ttf",
"Inter-Medium.ttf",
RequestBody.create(
MediaType.parse("font/ttf"),
new File("Inter-Medium.ttf")
)
)
.addFormDataPart(
"Inter-Bold.ttf",
"Inter-Bold.ttf",
RequestBody.create(
MediaType.parse("font/ttf"),
new File("Inter-Bold.ttf")
)
)
.addFormDataPart(
"SpaceMono-Regular.ttf",
"SpaceMono-Regular.ttf",
RequestBody.create(
MediaType.parse("font/ttf"),
new File("SpaceMono-Regular.ttf")
)
)
.addFormDataPart(
"instructions",
new JSONObject()
.put("parts", new JSONArray()
.put(new JSONObject()
.put("html", "index.html")
.put("assets", new JSONArray()
.put("style.css")
.put("Inter-Regular.ttf")
.put("Inter-Medium.ttf")
.put("Inter-Bold.ttf")
.put("SpaceMono-Regular.ttf")
)
)
).toString()
)
.build();
final Request request = new Request.Builder()
.url("https://api.nutrient.io/build")
.method("POST", body)
.addHeader("Authorization", "Bearer your_api_key_here")
.build();
final OkHttpClient client = new OkHttpClient()
.newBuilder()
.build();
final Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
Files.copy(
response.body().byteStream(),
FileSystems.getDefault().getPath("result.pdf"),
StandardCopyOption.REPLACE_EXISTING
);
} else {
// Handle the error
throw new IOException(response.body().string());
}
}
}
using System;
using System.IO;
using System.Net;
using RestSharp;
namespace PspdfkitApiDemo
{
class Program
{
static void Main(string[] args)
{
var client = new RestClient("https://api.nutrient.io/build");
var request = new RestRequest(Method.POST)
.AddHeader("Authorization", "Bearer your_api_key_here")
.AddFile("index.html", "index.html")
.AddFile("style.css", "style.css")
.AddFile("Inter-Regular.ttf", "Inter-Regular.ttf")
.AddFile("Inter-Medium.ttf", "Inter-Medium.ttf")
.AddFile("Inter-Bold.ttf", "Inter-Bold.ttf")
.AddFile("SpaceMono-Regular.ttf", "SpaceMono-Regular.ttf")
.AddParameter("instructions", new JsonObject
{
["parts"] = new JsonArray
{
new JsonObject
{
["html"] = "index.html",
["assets"] = new JsonArray
{
"style.css",
"Inter-Regular.ttf",
"Inter-Medium.ttf",
"Inter-Bold.ttf",
"SpaceMono-Regular.ttf"
}
}
}
}.ToString());
request.AdvancedResponseWriter = (responseStream, response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (responseStream)
{
using var outputFileWriter = File.OpenWrite("result.pdf");
responseStream.CopyTo(outputFileWriter);
}
}
else
{
var responseStreamReader = new StreamReader(responseStream);
Console.Write(responseStreamReader.ReadToEnd());
}
};
client.Execute(request);
}
}
}
// This code requires Node.js. Do not run this code directly in a web browser.
const axios = require('axios')
const FormData = require('form-data')
const fs = require('fs')
const formData = new FormData()
formData.append('instructions', JSON.stringify({
parts: [
{
html: "index.html",
assets: [
"style.css",
"Inter-Regular.ttf",
"Inter-Medium.ttf",
"Inter-Bold.ttf",
"SpaceMono-Regular.ttf"
]
}
]
}))
formData.append('index.html', fs.createReadStream('index.html'))
formData.append('style.css', fs.createReadStream('style.css'))
formData.append('Inter-Regular.ttf', fs.createReadStream('Inter-Regular.ttf'))
formData.append('Inter-Medium.ttf', fs.createReadStream('Inter-Medium.ttf'))
formData.append('Inter-Bold.ttf', fs.createReadStream('Inter-Bold.ttf'))
formData.append('SpaceMono-Regular.ttf', fs.createReadStream('SpaceMono-Regular.ttf'))
;(async () => {
try {
const response = await axios.post('https://api.nutrient.io/build', formData, {
headers: formData.getHeaders({
'Authorization': 'Bearer your_api_key_here'
}),
responseType: "stream"
})
response.data.pipe(fs.createWriteStream("result.pdf"))
} catch (e) {
const errorString = await streamToString(e.response.data)
console.log(errorString)
}
})()
function streamToString(stream) {
const chunks = []
return new Promise((resolve, reject) => {
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)))
stream.on("error", (err) => reject(err))
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")))
})
}
import requests
import json
response = requests.request(
'POST',
'https://api.nutrient.io/build',
headers = {
'Authorization': 'Bearer your_api_key_here'
},
files = {
'index.html': open('index.html', 'rb'),
'style.css': open('style.css', 'rb'),
'Inter-Regular.ttf': open('Inter-Regular.ttf', 'rb'),
'Inter-Medium.ttf': open('Inter-Medium.ttf', 'rb'),
'Inter-Bold.ttf': open('Inter-Bold.ttf', 'rb'),
'SpaceMono-Regular.ttf': open('SpaceMono-Regular.ttf', 'rb')
},
data = {
'instructions': json.dumps({
'parts': [
{
'html': 'index.html',
'assets': [
'style.css',
'Inter-Regular.ttf',
'Inter-Medium.ttf',
'Inter-Bold.ttf',
'SpaceMono-Regular.ttf'
]
}
]
})
},
stream = True
)
if response.ok:
with open('result.pdf', 'wb') as fd:
for chunk in response.iter_content(chunk_size=8096):
fd.write(chunk)
else:
print(response.text)
exit()
<?php
$FileHandle = fopen('result.pdf', 'w+');
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.nutrient.io/build',
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_POSTFIELDS => array(
'instructions' => '{
"parts": [
{
"html": "index.html",
"assets": [
"style.css",
"Inter-Regular.ttf",
"Inter-Medium.ttf",
"Inter-Bold.ttf",
"SpaceMono-Regular.ttf"
]
}
]
}',
'index.html' => new CURLFILE('index.html'),
'style.css' => new CURLFILE('style.css'),
'Inter-Regular.ttf' => new CURLFILE('Inter-Regular.ttf'),
'Inter-Medium.ttf' => new CURLFILE('Inter-Medium.ttf'),
'Inter-Bold.ttf' => new CURLFILE('Inter-Bold.ttf'),
'SpaceMono-Regular.ttf' => new CURLFILE('SpaceMono-Regular.ttf')
),
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer your_api_key_here'
),
CURLOPT_FILE => $FileHandle,
));
$response = curl_exec($curl);
curl_close($curl);
fclose($FileHandle);
POST https://api.nutrient.io/build HTTP/1.1
Content-Type: multipart/form-data; boundary=--customboundary
Authorization: Bearer your_api_key_here
--customboundary
Content-Disposition: form-data; name="instructions"
Content-Type: application/json
{
"parts": [
{
"html": "index.html",
"assets": [
"style.css",
"Inter-Regular.ttf",
"Inter-Medium.ttf",
"Inter-Bold.ttf",
"SpaceMono-Regular.ttf"
]
}
]
}
--customboundary
Content-Disposition: form-data; name="index.html"; filename="index.html"
Content-Type: text/html
(index.html data)
--customboundary
Content-Disposition: form-data; name="style.css"; filename="style.css"
Content-Type: text/css
(style.css data)
--customboundary
Content-Disposition: form-data; name="Inter-Regular.ttf"; filename="Inter-Regular.ttf"
Content-Type: font/ttf
(Inter-Regular.ttf data)
--customboundary
Content-Disposition: form-data; name="Inter-Medium.ttf"; filename="Inter-Medium.ttf"
Content-Type: font/ttf
(Inter-Medium.ttf data)
--customboundary
Content-Disposition: form-data; name="Inter-Bold.ttf"; filename="Inter-Bold.ttf"
Content-Type: font/ttf
(Inter-Bold.ttf data)
--customboundary
Content-Disposition: form-data; name="SpaceMono-Regular.ttf"; filename="SpaceMono-Regular.ttf"
Content-Type: font/ttf
(SpaceMono-Regular.ttf data)
--customboundary--
Forms
HTML
With your invoice looking good, let's look at another feature of PDF generation: fillable forms.
You can easily add forms to your PDF by adding regular HTML input tags to your HTML file. Add the following HTML snippet to your
index.html
file below the div
with the intro-table
class.
HTML template section:
<!-- Add below the intro-table div -->
<div class="page" style="page-break-after: always">
<div>
<h4>Thank you for your purchase!</h4>
</div>
<div class="form">
<label for="notes" class="label"> Notes: </label>
<input type="text" id="notes" class="border-bottom" value="" />
</div>
<div class="signer">
<div class="form signer-item">
<label for="date" class="label">Date:</label>
<input type="text" id="date" class="border-bottom" value="01/01/2021" />
</div>
<div class="form signer-item">
<label for="signature" class="label">Issued by:</label>
<input type="text" id="signature" class="border" value="Sign Here" />
</div>
</div>
</div>
CSS
You'll also make them look nice by adding some styling for the forms to your CSS file.
Add the following CSS snippet to your style.css
file.
If you rerun the generation now, with the updated HTML and CSS, you should have fillable PDF forms on the second page.
HTML template section:
/* Add below .intro-form-item-border:last-child */
.form {
display: flex;
flex-direction: column;
margin-top: 6rem;
}
.signer {
display: flex;
justify-content: space-between;
gap: 2.5rem;
margin: 2rem 0 2rem 0;
}
.signer-item {
flex-grow: 1;
}
input {
color: #4537de;
font-family: "Space Mono", monospace;
text-align: center;
margin-top: 1.5rem;
height: 4rem;
width: 100%;
box-sizing: border-box;
}
input#date,
input#notes {
text-align: left;
}
input#signature {
height: 8rem;
}
Headers and Footers
HTML
For your final trick, you'll add headers and footers to your invoice.
To make use of this, add any HTML element as the first or last element of the body, and give it the
pspdfkit-header
or pspdfkit-footer
ID.
Our PDF Generation support will take care of making sure this element is shown on all pages if the generated PDF ends up having multiple pages, and we'll also replace the
{{ pageNumber }}
and {{ pageCount }}
placeholders with the correct values.
For this example, add the HTML for the headers shown in the sample code as the first and last elements of the
body
element in the index.html
file.
In the header, you'll also include your logo, which you can find as part of our
invoice example.
Put logo.svg
in the same folder as your index.html
.
You'll include the logo in the code later.
HTML template section:
<!-- Add as the first element in the body. -->
<div id="pspdfkit-header">
<div class="header-columns">
<div class="logotype">
<img class="logo" src="logo.svg" />
<p>Company</p>
</div>
<div>
<p>[Company Info]</p>
</div>
</div>
</div>
<!-- Add as the last element in the body. -->
<div id="pspdfkit-footer">
<div class="footer-columns">
<span>Invoice</span>
<span>Page {{ pageNumber }} of {{ pageCount }}</span>
</div>
</div>
CSS
You should also make sure your headers and footers look good by updating your CSS file one last time. Add the following to your style.css
.
HTML template section:
/* Add below input#signature. */
#pspdfkit-header {
font-size: 0.625rem;
text-transform: uppercase;
letter-spacing: 2px;
font-weight: 400;
color: #717885;
margin-top: 2.5rem;
margin-bottom: 2.5rem;
width: 100%;
}
.header-columns {
display: flex;
justify-content: space-between;
padding-left: 2.5rem;
padding-right: 2.5rem;
}
.logo {
height: 1.5rem;
width: auto;
margin-right: 1rem;
}
.logotype {
display: flex;
align-items: center;
font-weight: 700;
}
#pspdfkit-footer {
font-size: 0.5rem;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 500;
color: #717885;
margin-top: 2.5rem;
bottom: 2.5rem;
position: absolute;
width: 100%;
}
.footer-columns {
display: flex;
justify-content: space-between;
padding-left: 2.5rem;
padding-right: 2.5rem;
}
Code
Finally, you'll include logo.svg
in your request.
And with that, you've used all features PDF Generation provides. More specifically, you used HTML to define the structure of your PDF, used CSS to style and lay out your content, added fonts and images to the PDF, and even included fillable forms.
The full invoice example contains everything you added here, and it adds the actual invoice and summary in the middle. It's a great starting point for your own PDF Generation templates. You can also check out all our other fully featured examples at the bottom of the page.
Command to generate a PDF file:
curl -X POST https://api.nutrient.io/build \
-H "Authorization: Bearer your_api_key_here" \
-o result.pdf \
--fail \
-F [email protected] \
-F [email protected] \
-F [email protected] \
-F [email protected] \
-F [email protected] \
-F [email protected] \
-F [email protected] \
-F instructions='{
"parts": [
{
"html": "index.html",
"assets": [
"style.css",
"Inter-Regular.ttf",
"Inter-Medium.ttf",
"Inter-Bold.ttf",
"SpaceMono-Regular.ttf",
"logo.svg"
]
}
]
}'
curl -X POST https://api.nutrient.io/build ^
-H "Authorization: Bearer your_api_key_here" ^
-o result.pdf ^
--fail ^
-F [email protected] ^
-F [email protected] ^
-F [email protected] ^
-F [email protected] ^
-F [email protected] ^
-F [email protected] ^
-F [email protected] ^
-F instructions="{\"parts\": [{\"html\": \"index.html\", \"assets\": [\"style.css\", \"Inter-Regular.ttf\", \"Inter-Medium.ttf\", \"Inter-Bold.ttf\", \"SpaceMono-Regular.ttf\", \"logo.svg\"]}]}"
package com.example.pspdfkit;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import org.json.JSONArray;
import org.json.JSONObject;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public final class PspdfkitApiExample {
public static void main(final String[] args) throws IOException {
final RequestBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(
"index.html",
"index.html",
RequestBody.create(
MediaType.parse("text/html"),
new File("index.html")
)
)
.addFormDataPart(
"style.css",
"style.css",
RequestBody.create(
MediaType.parse("text/css"),
new File("style.css")
)
)
.addFormDataPart(
"Inter-Regular.ttf",
"Inter-Regular.ttf",
RequestBody.create(
MediaType.parse("font/ttf"),
new File("Inter-Regular.ttf")
)
)
.addFormDataPart(
"Inter-Medium.ttf",
"Inter-Medium.ttf",
RequestBody.create(
MediaType.parse("font/ttf"),
new File("Inter-Medium.ttf")
)
)
.addFormDataPart(
"Inter-Bold.ttf",
"Inter-Bold.ttf",
RequestBody.create(
MediaType.parse("font/ttf"),
new File("Inter-Bold.ttf")
)
)
.addFormDataPart(
"SpaceMono-Regular.ttf",
"SpaceMono-Regular.ttf",
RequestBody.create(
MediaType.parse("font/ttf"),
new File("SpaceMono-Regular.ttf")
)
)
.addFormDataPart(
"logo.svg",
"logo.svg",
RequestBody.create(
MediaType.parse("image/svg+xml"),
new File("logo.svg")
)
)
.addFormDataPart(
"instructions",
new JSONObject()
.put("parts", new JSONArray()
.put(new JSONObject()
.put("html", "index.html")
.put("assets", new JSONArray()
.put("style.css")
.put("Inter-Regular.ttf")
.put("Inter-Medium.ttf")
.put("Inter-Bold.ttf")
.put("SpaceMono-Regular.ttf")
.put("logo.svg")
)
)
).toString()
)
.build();
final Request request = new Request.Builder()
.url("https://api.nutrient.io/build")
.method("POST", body)
.addHeader("Authorization", "Bearer your_api_key_here")
.build();
final OkHttpClient client = new OkHttpClient()
.newBuilder()
.build();
final Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
Files.copy(
response.body().byteStream(),
FileSystems.getDefault().getPath("result.pdf"),
StandardCopyOption.REPLACE_EXISTING
);
} else {
// Handle the error
throw new IOException(response.body().string());
}
}
}
using System;
using System.IO;
using System.Net;
using RestSharp;
namespace PspdfkitApiDemo
{
class Program
{
static void Main(string[] args)
{
var client = new RestClient("https://api.nutrient.io/build");
var request = new RestRequest(Method.POST)
.AddHeader("Authorization", "Bearer your_api_key_here")
.AddFile("index.html", "index.html")
.AddFile("style.css", "style.css")
.AddFile("Inter-Regular.ttf", "Inter-Regular.ttf")
.AddFile("Inter-Medium.ttf", "Inter-Medium.ttf")
.AddFile("Inter-Bold.ttf", "Inter-Bold.ttf")
.AddFile("SpaceMono-Regular.ttf", "SpaceMono-Regular.ttf")
.AddFile("logo.svg", "logo.svg")
.AddParameter("instructions", new JsonObject
{
["parts"] = new JsonArray
{
new JsonObject
{
["html"] = "index.html",
["assets"] = new JsonArray
{
"style.css",
"Inter-Regular.ttf",
"Inter-Medium.ttf",
"Inter-Bold.ttf",
"SpaceMono-Regular.ttf",
"logo.svg"
}
}
}
}.ToString());
request.AdvancedResponseWriter = (responseStream, response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (responseStream)
{
using var outputFileWriter = File.OpenWrite("result.pdf");
responseStream.CopyTo(outputFileWriter);
}
}
else
{
var responseStreamReader = new StreamReader(responseStream);
Console.Write(responseStreamReader.ReadToEnd());
}
};
client.Execute(request);
}
}
}
// This code requires Node.js. Do not run this code directly in a web browser.
const axios = require('axios')
const FormData = require('form-data')
const fs = require('fs')
const formData = new FormData()
formData.append('instructions', JSON.stringify({
parts: [
{
html: "index.html",
assets: [
"style.css",
"Inter-Regular.ttf",
"Inter-Medium.ttf",
"Inter-Bold.ttf",
"SpaceMono-Regular.ttf",
"logo.svg"
]
}
]
}))
formData.append('index.html', fs.createReadStream('index.html'))
formData.append('style.css', fs.createReadStream('style.css'))
formData.append('Inter-Regular.ttf', fs.createReadStream('Inter-Regular.ttf'))
formData.append('Inter-Medium.ttf', fs.createReadStream('Inter-Medium.ttf'))
formData.append('Inter-Bold.ttf', fs.createReadStream('Inter-Bold.ttf'))
formData.append('SpaceMono-Regular.ttf', fs.createReadStream('SpaceMono-Regular.ttf'))
formData.append('logo.svg', fs.createReadStream('logo.svg'))
;(async () => {
try {
const response = await axios.post('https://api.nutrient.io/build', formData, {
headers: formData.getHeaders({
'Authorization': 'Bearer your_api_key_here'
}),
responseType: "stream"
})
response.data.pipe(fs.createWriteStream("result.pdf"))
} catch (e) {
const errorString = await streamToString(e.response.data)
console.log(errorString)
}
})()
function streamToString(stream) {
const chunks = []
return new Promise((resolve, reject) => {
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)))
stream.on("error", (err) => reject(err))
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")))
})
}
import requests
import json
response = requests.request(
'POST',
'https://api.nutrient.io/build',
headers = {
'Authorization': 'Bearer your_api_key_here'
},
files = {
'index.html': open('index.html', 'rb'),
'style.css': open('style.css', 'rb'),
'Inter-Regular.ttf': open('Inter-Regular.ttf', 'rb'),
'Inter-Medium.ttf': open('Inter-Medium.ttf', 'rb'),
'Inter-Bold.ttf': open('Inter-Bold.ttf', 'rb'),
'SpaceMono-Regular.ttf': open('SpaceMono-Regular.ttf', 'rb'),
'logo.svg': open('logo.svg', 'rb')
},
data = {
'instructions': json.dumps({
'parts': [
{
'html': 'index.html',
'assets': [
'style.css',
'Inter-Regular.ttf',
'Inter-Medium.ttf',
'Inter-Bold.ttf',
'SpaceMono-Regular.ttf',
'logo.svg'
]
}
]
})
},
stream = True
)
if response.ok:
with open('result.pdf', 'wb') as fd:
for chunk in response.iter_content(chunk_size=8096):
fd.write(chunk)
else:
print(response.text)
exit()
<?php
$FileHandle = fopen('result.pdf', 'w+');
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://api.nutrient.io/build',
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_POSTFIELDS => array(
'instructions' => '{
"parts": [
{
"html": "index.html",
"assets": [
"style.css",
"Inter-Regular.ttf",
"Inter-Medium.ttf",
"Inter-Bold.ttf",
"SpaceMono-Regular.ttf",
"logo.svg"
]
}
]
}',
'index.html' => new CURLFILE('index.html'),
'style.css' => new CURLFILE('style.css'),
'Inter-Regular.ttf' => new CURLFILE('Inter-Regular.ttf'),
'Inter-Medium.ttf' => new CURLFILE('Inter-Medium.ttf'),
'Inter-Bold.ttf' => new CURLFILE('Inter-Bold.ttf'),
'SpaceMono-Regular.ttf' => new CURLFILE('SpaceMono-Regular.ttf'),
'logo.svg' => new CURLFILE('logo.svg')
),
CURLOPT_HTTPHEADER => array(
'Authorization: Bearer your_api_key_here'
),
CURLOPT_FILE => $FileHandle,
));
$response = curl_exec($curl);
curl_close($curl);
fclose($FileHandle);
POST https://api.nutrient.io/build HTTP/1.1
Content-Type: multipart/form-data; boundary=--customboundary
Authorization: Bearer your_api_key_here
--customboundary
Content-Disposition: form-data; name="instructions"
Content-Type: application/json
{
"parts": [
{
"html": "index.html",
"assets": [
"style.css",
"Inter-Regular.ttf",
"Inter-Medium.ttf",
"Inter-Bold.ttf",
"SpaceMono-Regular.ttf",
"logo.svg"
]
}
]
}
--customboundary
Content-Disposition: form-data; name="index.html"; filename="index.html"
Content-Type: text/html
(index.html data)
--customboundary
Content-Disposition: form-data; name="style.css"; filename="style.css"
Content-Type: text/css
(style.css data)
--customboundary
Content-Disposition: form-data; name="Inter-Regular.ttf"; filename="Inter-Regular.ttf"
Content-Type: font/ttf
(Inter-Regular.ttf data)
--customboundary
Content-Disposition: form-data; name="Inter-Medium.ttf"; filename="Inter-Medium.ttf"
Content-Type: font/ttf
(Inter-Medium.ttf data)
--customboundary
Content-Disposition: form-data; name="Inter-Bold.ttf"; filename="Inter-Bold.ttf"
Content-Type: font/ttf
(Inter-Bold.ttf data)
--customboundary
Content-Disposition: form-data; name="SpaceMono-Regular.ttf"; filename="SpaceMono-Regular.ttf"
Content-Type: font/ttf
(SpaceMono-Regular.ttf data)
--customboundary
Content-Disposition: form-data; name="logo.svg"; filename="logo.svg"
Content-Type: image/svg+xml
(logo.svg data)
--customboundary--
Download Sample Documents
Try generating PDFs with different HTML templates. Download our various sample documents and customize them to your needs.