# PDF generation

This guide explains how to create a PDF from HTML using Nutrient DWS Processor API via `POST https://api.nutrient.io/build` with an `html` part. For a step-by-step walkthrough, refer to the [PDF generator API](https://www.nutrient.io/guides/dws-processor/tools-and-api/pdf-generator-api.md) guide. For an overview of the HTML-to-PDF API with signup, pricing, and code examples, see the [HTML-to-PDF API task page](/api/html-to-pdf-api/).

Below is a schema outlining all the available options:

```ts

type Orientation = "landscape" | "portrait";
type PageSize =
  | "A0"
  | "A1"
  | "A2"
  | "A3"
  | "A4"
  | "A5"
  | "A6"
  | "A7"
  | "A8"
  | "Letter"
  | "Legal";

// Represents one part that was sent in the multipart request. Should be the
// `name` that was specified for the part.
type MultipartReference = string;

type HTMLPart = {
  html: MultipartReference; // The HTML file passed in the multipart request.
  assets?: Array<MultipartReference>; // All assets imported in the HTML. Reference the name passed in the multipart request.
  layout?: {
    orientation?: Orientation;
    size?:
      | {
          width: number;
          height: number;
        }
      | PageSize; // {width, height} in mm or page size preset.
    margin?: {
      // Margin sizes in mm.
      left: number;
      top: number;
      right: number;
      bottom: number;
    };
  };
};

```

The only mandatory key for an HTML part is the `html` key pointing to the main HTML file to use for generating the PDF. All other keys are optional.

## Referencing assets

When designing an HTML page, it’s common to split the design into multiple files, such as an HTML file, a CSS file, fonts, and image files. The PDF generation command expects a flat directory structure, so any referenced assets have to reside next to the HTML file and not in subdirectories.

The following shows how you’d send a CSS file that’s referenced in the HTML file:

```html

<!DOCTYPE html>
<head>
  <link rel="stylesheet" href="style.css" />
</head>
<html>
  <body>
    <h1>PDF Generation Header</h1>
    <img src="my-image.jpg" />
  </body>
</html>

```

```css

@font-face {
  font-family: "Open Sans";
  src: url("OpenSans-Regular.ttf") format("truetype");
}

h1 {
  font-size: xx-large;
  font-family: "Open Sans", sans-serif;
}

```

The request to create a PDF from these assets would look like this:

### Shell

```shell

curl -X POST https://api.nutrient.io/build \
  -H "Authorization: Bearer your_api_key_here" \
  -o result.pdf \
  --fail \
  -F page.html=@/path/to/page.html \
  -F style.css=@/path/to/style.css \
  -F my-image.jpg=@/path/to/my-image.jpg \
  -F OpenSans-Regular.ttf=@/path/to/OpenSans-Regular.ttf \
  -F instructions='{
      "parts": [
        {
          "html": "page.html",
          "assets": [
            "style.css",
            "my-image.jpg",
            "OpenSans-Regular.ttf"
          ]
        }
      ]
    }'

```

### Shell (Windows)

```powershell

curl -X POST https://api.nutrient.io/build ^
  -H "Authorization: Bearer your_api_key_here" ^
  -o result.pdf ^
  --fail ^
  -F page.html=@/path/to/page.html ^
  -F style.css=@/path/to/style.css ^
  -F my-image.jpg=@/path/to/my-image.jpg ^
  -F OpenSans-Regular.ttf=@/path/to/OpenSans-Regular.ttf ^
  -F instructions="{\"parts\": [{\"html\": \"page.html\", \"assets\": [\"style.css\", \"my-image.jpg\", \"OpenSans-Regular.ttf\"]}]}"

```

### Java

```java

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(
        "page.html",
        "/path/to/page.html",
        RequestBody.create(
          MediaType.parse("text/html"),
          new File("/path/to/page.html")
        )
      ).addFormDataPart(
        "style.css",
        "/path/to/style.css",
        RequestBody.create(
          MediaType.parse("text/css"),
          new File("/path/to/style.css")
        )
      ).addFormDataPart(
        "my-image.jpg",
        "/path/to/my-image.jpg",
        RequestBody.create(
          MediaType.parse("image/jpeg"),
          new File("/path/to/my-image.jpg")
        )
      ).addFormDataPart(
        "OpenSans-Regular.ttf",
        "/path/to/OpenSans-Regular.ttf",
        RequestBody.create(
          MediaType.parse("font/ttf"),
          new File("/path/to/OpenSans-Regular.ttf")
        )
      ).addFormDataPart(
        "instructions",
        new JSONObject().put("parts", new JSONArray().put(new JSONObject().put("html", "page.html").put("assets", new JSONArray().put("style.css").put("my-image.jpg").put("OpenSans-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());
    }
  }
}

```

### C#

```csharp

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("page.html", "/path/to/page.html").AddFile("style.css", "/path/to/style.css").AddFile("my-image.jpg", "/path/to/my-image.jpg").AddFile("OpenSans-Regular.ttf", "/path/to/OpenSans-Regular.ttf").AddParameter("instructions", new JsonObject
        {
          ["parts"] = new JsonArray
          {
            new JsonObject
            {
              ["html"] = "page.html",
              ["assets"] = new JsonArray
              {
                "style.css",
                "my-image.jpg",
                "OpenSans-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);
    }
  }
}

```

### JavaScript

```javascript

// 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: "page.html",
        assets: ["style.css", "my-image.jpg", "OpenSans-Regular.ttf"],
      },
    ],
  }),
);
formData.append("page.html", fs.createReadStream("/path/to/page.html"));
formData.append("style.css", fs.createReadStream("/path/to/style.css"));
formData.append("my-image.jpg", fs.createReadStream("/path/to/my-image.jpg"));
formData.append(
  "OpenSans-Regular.ttf",
  fs.createReadStream("/path/to/OpenSans-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")));
  });
}

```

### Python

```python

import requests
import json

response = requests.request(
  'POST',
  'https://api.nutrient.io/build',
  headers = {
    'Authorization': 'Bearer your_api_key_here'
  },
  files = {
    'page.html': open('/path/to/page.html', 'rb'),
    'style.css': open('/path/to/style.css', 'rb'),
    'my-image.jpg': open('/path/to/my-image.jpg', 'rb'),
    'OpenSans-Regular.ttf': open('/path/to/OpenSans-Regular.ttf', 'rb')
  },
  data = {
    'instructions': json.dumps({
      'parts': [
        {
          'html': 'page.html',
          'assets': [
            'style.css',
            'my-image.jpg',
            'OpenSans-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

```php

<?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": "page.html",
          "assets": [
            "style.css",
            "my-image.jpg",
            "OpenSans-Regular.ttf"
          ]
        }
      ]
    }',
    'page.html' => new CURLFILE('/path/to/page.html'),
    'style.css' => new CURLFILE('/path/to/style.css'),
    'my-image.jpg' => new CURLFILE('/path/to/my-image.jpg'),
    'OpenSans-Regular.ttf' => new CURLFILE('/path/to/OpenSans-Regular.ttf')
  ),
  CURLOPT_HTTPHEADER => array(
    'Authorization: Bearer your_api_key_here'
  ),
  CURLOPT_FILE => $FileHandle,
));

$response = curl_exec($curl);

curl_close($curl);

fclose($FileHandle);

```

### HTTP

```http

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": "page.html",
      "assets": [
        "style.css",
        "my-image.jpg",
        "OpenSans-Regular.ttf"
      ]
    }
  ]
}
--customboundary
Content-Disposition: form-data; name="page.html"; filename="/path/to/page.html"
Content-Type: text/html

(page.html data)
--customboundary
Content-Disposition: form-data; name="style.css"; filename="/path/to/style.css"
Content-Type: text/css

(style.css data)
--customboundary
Content-Disposition: form-data; name="my-image.jpg"; filename="/path/to/my-image.jpg"
Content-Type: image/jpeg

(my-image.jpg data)
--customboundary
Content-Disposition: form-data; name="OpenSans-Regular.ttf"; filename="/path/to/OpenSans-Regular.ttf"
Content-Type: font/ttf

(OpenSans-Regular.ttf data)
--customboundary--

```

> JavaScript assets currently aren’t supported.

Assets passed in the multipart request must match the name used to reference the file in HTML. For example, if you have an image block, `<img src="my-image.jpg">`, the data representing the image in the multipart request should have the name `my-image.jpg`.

## Page layout

The `layout` object, which is part of the HTML part, allows for customization of the PDF page layout and dimensions. All figures in this object are in reference to millimeters, and all pages will take on this configuration.

## Fillable forms

Nutrient DWS Processor API can also generate PDFs containing fillable form fields from HTML.

Because not all HTML form fields map directly to PDF forms, a subset of form fields is supported. The following table shows how the HTML `input` element types map to PDF form types.

| `input` type | PDF form field                                    |
| ------------ | ------------------------------------------------- |
| `text`       | Text box                                          |
| `password`   | Text box where all characters are replaced with * |
| `radio`      | Radio button                                      |
| `checkbox`   | Checkbox                                          |
| `select`     | Combo box                                         |

All other `input` types aren’t supported and won’t be converted to PDF form fields.

### Form values

All form values and radio buttons/checkboxes that are checked in HTML will be carried over to the form field values in the generated PDF.

## Headers and footers

Nutrient DWS Processor API can add custom header and footer sections that will repeat across all pages in the resulting PDF. You can also dynamically display the current page number and page count, as well as style it as desired.

### Adding a header

Nutrient DWS Processor API will look for a container element using `pspdfkit-header` as its ID, defined in your template, and it’ll repeat it at the top of each of the generated pages:

```html

<div id="pspdfkit-header">
  <div class="header-columns">
    <div class="logotype">
      <img class="logo" src="logo.svg" />
      <p>ACME Inc.</p>
    </div>

    <div>
      <p>Always improving.</p>
    </div>
  </div>
</div>

```

Before generating the PDF, the following CSS will be added before any style sheet already defined in the template. We don’t recommend overriding these properties, as it might break the header position:

```css

#pspdfkit-header {

  display: flow-root;
}

```

The `display: flow-root;` declaration defines a new [block formatting context](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context) and suppresses [margin collapsing](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing) so that any margin defined inside the header is preserved entirely across all pages in the resulting PDF.

> The element with `pspdfkit-header` as its ID must be the first child of the document’s body, if specified.

### Adding a footer

To add a footer that you want repeated across all pages of the resulting PDF, you have to define a container element with the `pspdfkit-footer` ID. It must be the last child of the document’s body:

```html

<div id="pspdfkit-footer">
  <div class="footer-columns">
    <span>10/18/2021</span>
    <span>Page {{ pageNumber }} of {{ pageCount }}</span>
  </div>
</div>

```

Before generating the PDF, the following CSS will be added before any style sheet already defined in the template. We don’t recommend overriding these properties, as it might break the footer position:

```css

#pspdfkit-footer {

  display: flow-root;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  box-sizing: border-box;
  width: 100%;
}

```

The `display: flow-root;` declaration defines a new [block formatting context](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context) and suppresses [margin collapsing](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing) so that any margin defined inside the footer is preserved entirely across all pages in the resulting PDF.

### Displaying the current page number and page count

To display the current page number as part of the header and/or the footer, use the special `{{ pageNumber }}` token as a placeholder to tell Nutrient where to display the value on each page. Similarly, for the total page count, use `{{ pageCount }}`:

```html

<p>Page {{ pageNumber }} of <strong>{{ pageCount }}</strong></p>

```

### Customizing the header and footer

When specifying a header and footer, you can make use of any HTML and CSS as usual, and all styles added to the main page will affect the header and footer just as expected with regular DOM elements.

You can add images, use custom fonts, set a background, change font size, etc.

To set a gap between the header and the content, you can use regular CSS techniques, such as specifying a `margin-bottom` declaration to the `#pspdfkit-header` selector via CSS.

Similarly, you can also use CSS to set a gap between the content on each page and the footer — for instance, via the `margin-top` property in the `#pspdfkit-footer` selector.

> The margins that are set to the page layout as part of the generation configuration will affect the space between the edges of the generated pages and the header and footer, respectively.

### Sample templates

We have a collection of sample templates available for you to try. Feel free to download them and modify them as needed.

[![Business letter](@/assets/guides/dws-processor/business-letter.png)](/assets/guides/dws-processor/business-letter.zip)

**Business letter**

[![Certificate](@/assets/guides/dws-processor/certificate.png)](/assets/guides/dws-processor/certificate.zip)

**Certificate**

[![Invoice](@/assets/guides/dws-processor/invoice.png)](/assets/guides/dws-processor/invoice.zip)

**Invoice**

[![Presentation](@/assets/guides/dws-processor/presentation.png)](/assets/guides/dws-processor/presentation.zip)

**Presentation**

[![Purchase order](@/assets/guides/dws-processor/purchase-order.png)](/assets/guides/dws-processor/purchase-order.zip)

**Purchase order**

[![Quote](@/assets/guides/dws-processor/quote.png)](/assets/guides/dws-processor/quote.zip)

**Quote**

[![Receipt](@/assets/guides/dws-processor/receipt.png)](/assets/guides/dws-processor/receipt.zip)

**Receipt**

[![Report](@/assets/guides/dws-processor/report.png)](/assets/guides/dws-processor/report.zip)

**Report**
---

## Related pages

- [Deployment options](/guides/dws-processor/developer-guides/deployment-options.md)
- [Authentication your requests](/guides/dws-processor/developer-guides/authentication.md)
- [Combine API actions in a single request](/guides/dws-processor/developer-guides/combine-workflows.md)
- [Troubleshooting errors](/guides/dws-processor/developer-guides/errors.md)
- [API overview](/guides/dws-processor/developer-guides.md)
- [Performance](/guides/dws-processor/developer-guides/performance.md)
- [Web SDK integration](/guides/dws-processor/developer-guides/web-sdk-client.md)

