How to Convert PDF Forms from XFA to AcroForms with Java
This tutorial shows you how to convert PDF forms from XFA to AcroForms from a Java application with the XFA to AcroForms API Tool. The request sends an XFA PDF to /pdf-with-acroforms and returns a separate PDF containing standard AcroForm fields. The walkthrough gives you a practical starting point that you can adapt when you need to convert PDF forms from XFA to AcroForms in your own document workflow.
Why Convert PDF Forms from XFA to AcroForms with Java?
An organization modernizing a forms workflow may receive dynamic XFA documents that are not handled consistently by every viewer or downstream tool. Converting them to AcroForms produces standard PDF form fields for later processing.
The Java sample uploads the XFA PDF as file and calls /pdf-with-acroforms. The response contains a new PDF resource; it does not modify the uploaded source in place.
Java Code Example
import io.github.cdimascio.dotenv.Dotenv;
import java.io.File;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.json.JSONObject;
public class PDFWithAcroforms {
// By default, we use the US-based API service. This is the primary endpoint for global use.
private static final String API_URL = "https://api.pdfrest.com";
// For GDPR compliance and enhanced performance for European users, you can switch to the EU-based
// service by commenting out the URL above and uncommenting the URL below.
// For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
// private static final String API_URL = "https://eu-api.pdfrest.com";
// Specify the path to your file here, or as the first argument when running the program.
private static final String DEFAULT_FILE_PATH = "/path/to/file.pdf";
// Specify your API key here, or in the environment variable PDFREST_API_KEY.
// You can also put the environment variable in a .env file.
private static final String DEFAULT_API_KEY = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
public static void main(String[] args) {
File inputFile;
if (args.length > 0) {
inputFile = new File(args[0]);
} else {
inputFile = new File(DEFAULT_FILE_PATH);
}
final Dotenv dotenv = Dotenv.configure().ignoreIfMalformed().ignoreIfMissing().load();
final RequestBody inputFileRequestBody =
RequestBody.create(inputFile, MediaType.parse("application/pdf"));
RequestBody requestBody =
new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", inputFile.getName(), inputFileRequestBody)
.build();
Request request =
new Request.Builder()
.header("Api-Key", dotenv.get("PDFREST_API_KEY", DEFAULT_API_KEY))
.url(API_URL + "/pdf-with-acroforms")
.post(requestBody)
.build();
try {
OkHttpClient client = new OkHttpClient().newBuilder().build();
Response response = client.newCall(request).execute();
System.out.println("Result code " + response.code());
if (response.body() != null) {
System.out.println(prettyJson(response.body().string()));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static String prettyJson(String json) {
// https://stackoverflow.com/a/9583835/11996393
return new JSONObject(json).toString(4);
}
}
Source: Java sample on GitHub.
Breaking Down the Code
Load the Java HTTP and JSON dependencies
import io.github.cdimascio.dotenv.Dotenv; import java.io.File; import java.io.IOException; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import org.json.JSONObject;
This dependency set assigns a clear role to each library: HTTP communication, credential lookup, or JSON handling.
Choose the input and load credentials
// service by commenting out the URL above and uncommenting the URL below.
// For more information visit https://pdfrest.com/pricing#how-do-eu-gdpr-api-calls-work
// private static final String API_URL = "https://eu-api.pdfrest.com";
// Specify the path to your file here, or as the first argument when running the program.
private static final String DEFAULT_FILE_PATH = "/path/to/file.pdf";
// Specify your API key here, or in the environment variable PDFREST_API_KEY.
// You can also put the environment variable in a .env file.
private static final String DEFAULT_API_KEY = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
public static void main(String[] args) {
File inputFile;
if (args.length > 0) {
inputFile = new File(args[0]);
} else {
inputFile = new File(DEFAULT_FILE_PATH);
}
final Dotenv dotenv = Dotenv.configure().ignoreIfMalformed().ignoreIfMissing().load();
final RequestBody inputFileRequestBody =
The startup code keeps a visible default path, accepts an override from the command line, and reads the API key through dotenv.
Build the multipart request body
final RequestBody inputFileRequestBody =
RequestBody.create(inputFile, MediaType.parse("application/pdf"));
RequestBody requestBody =
new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", inputFile.getName(), inputFileRequestBody)
.build();
Request request =
new Request.Builder()
.header("Api-Key", dotenv.get("PDFREST_API_KEY", DEFAULT_API_KEY))
.url(API_URL + "/pdf-with-acroforms")
.post(requestBody)
.build();
try {
OkHttpClient client = new OkHttpClient().newBuilder().build();
Response response = client.newCall(request).execute();
System.out.println("Result code " + response.code());
The Java sample uploads the XFA PDF as file and calls /pdf-with-acroforms. The response contains a new PDF resource; it does not modify the uploaded source in place. The multipart section packages fields and files together without hard-coding a Content-Type boundary.
Send the request to pdfRest
.build();
Request request =
new Request.Builder()
.header("Api-Key", dotenv.get("PDFREST_API_KEY", DEFAULT_API_KEY))
.url(API_URL + "/pdf-with-acroforms")
.post(requestBody)
.build();
try {
OkHttpClient client = new OkHttpClient().newBuilder().build();
Response response = client.newCall(request).execute();
System.out.println("Result code " + response.code());
The request builder posts the multipart content to pdfRest and keeps deployment-region changes independent of the rest of the Java code.
Read the API response
.build();
try {
OkHttpClient client = new OkHttpClient().newBuilder().build();
Response response = client.newCall(request).execute();
System.out.println("Result code " + response.code());
if (response.body() != null) {
System.out.println(prettyJson(response.body().string()));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static String prettyJson(String json) {
// https://stackoverflow.com/a/9583835/11996393
return new JSONObject(json).toString(4);
The response block prints the HTTP status and JSON body so generated resource IDs or API error details are visible.
Beyond the Tutorial
You have now seen how to convert PDF forms from XFA to AcroForms from a Java application. With that foundation, this conversion can prepare legacy forms for data import, field flattening, validation, and distribution.
When you move beyond the sample, keep in mind that acroForms and XFA do not provide identical behavior in every document, particularly when calculations, scripts, or dynamic layout are involved. Test field names, values, validation, and downstream import or flattening steps against the converted form before replacing a legacy workflow.
You can experiment with the Convert PDF Forms from XFA to Acroforms operation in API Lab. The XFA to AcroForms API Tool documentation describes the full parameter and response contract.
Note: The Convert PDF Forms from XFA to Acroforms example above uses multipart form data; to avoid transferring the same input again, follow the JSON payload example.