Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Sharadha1
Active Contributor
Recently we had a requirement for generating invoice PDFs from a cloud application. We had the entire application deployed in the cloud and did not want to use the Adobe Document Services(ADS) deployed in the  on-premise Netweaver system to generate the PDF forms.

We tried the Adobe service offered in SAP Cloud platform - 'SAP Forms by Adobe'. Though the official document covers most of the steps, it does not provide an end-to-end solution. This blog will cover all the steps we performed to implement the solution in the trial landscape.

You can use the SAP Forms by Adobe in two different scenarios

  1. From an application running on an ABAP or Java backend

  2. From an application through the SAP Forms by Adobe REST API via HTTP


The use case in question falls under the second scenario. In order to call the Rest API to generate the PDF forms, we need the following:

a. Form Template - PDF Layout

b. Data to be displayed in the form - Invoice data.

Pass these two parameters while calling the Rest API and get the PDF form back. I have used a simple Java application deployed in the SAP Cloud Platform to call the Adobe forms API.

Some steps are well documented here by SAP. So, I will be covering the additional steps in a more detailed manner.

  1. Enable the 'SAP Forms by Adobe' in your trial subaccount. Refer link

  2. Assign roles to users and update the destination. Refer link

  3. Before we can start with the Java application, register an oAuth client in the cloud platform subaccount. We need this to authenticate the java application to call the Adobe service. Refer link


Do not forget to note down the Client ID and the secret which was entered in Step 3. This is required to authenticate the Java application.

All the steps required in the SAP Cloud Platform to use the Adobe form services are complete. Now we need to design the layout of the PDF to be generated and prepare the data to be displayed in the PDF.

  1. Form Template


We will use the Adobe LiveCycle Designer to design the form template. For information about how and where to download this tool, along with licensing information, refer this link.

I have designed a very simple form layout for the blog. This tool can be used to design both Interactive and Non-interactive forms. You can refer the steps in the link to design and create data connections for the form template.



After the layout is complete, save the form template as .xdp file. The file will look something like this. This file has all the layout information required to render the PDF, which is used by the Adobe services.



  1. Data to be displayed.


Once you have the form template ready, you can generate the XML data file with sample data. Go to File->Form Properties. Choose Preview.



Click ‘Generate Preview Data’ and enter a location and name for the XML file to be generated.



Click on ‘Generate’. The XML file with some sample data will be generated and placed in the location. The XML file with sample data looks like this.



This is the exact format which needs to be passed to the Adobe service for it to pick up the data and place it in the exact location as designed in the form template.

Now that we have both the files which are required to make the REST API call, let us proceed with the java application.

Note that the application which calls the REST APIs to generate the PDF should create and send both the files to the Adobe services on cloud to get the PDF file as an output.

6. Java application

We perform the following steps in the java application.

  1. Retrieve Authorisation Token – oAuth token from SCP.

  2. Call the REST API using the oAuth token, the form template (.xdp file) and the data (.xml file).



  1. Retrieve oAuth token


Below is the java code I used to get the oAuth token. Make sure that you enter the Client ID and the client secret which was used in Step 3, when the oAuth client was registered.
/**
* Retrieve the auth token from Adobe service
* @return
*/
private String getoauthToken() {
String token = "";
String tokenEndpoint = "https://oauthasservices-sXXXXXXXtrial.hanatrial.ondemand.com/oauth2/api/v1/token";

String client_id = <<ClientID from step 3>>;
String client_secret = <<Client secret from step 3>>;

String grant_type = "client_credentials";
String scope = "generate-ads-output";

HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(tokenEndpoint);

String base64Credentials = Base64.getEncoder().encodeToString((client_id + ":" + client_secret).getBytes());
//1. Prepare the request headers
httpPost.addHeader("Authorization", "Basic " + base64Credentials);
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");

StringEntity input = null;
try {
input = new StringEntity("grant_type=" + grant_type + "&scope=" + scope);
httpPost.setEntity(input);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//2. Post the request
HttpResponse response = null;
try {
response = httpClient.execute(httpPost);
} catch (IOException e) {
e.printStackTrace();
}
//3. Retrieve the token from the response
try {
JSONObject tokenjson = new JSONObject(IOUtils.toString(response.getEntity().getContent(), "UTF-8"));
token = tokenjson.getString("access_token");
} catch (IOException e) {
e.printStackTrace();
} catch (UnsupportedOperationException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return token;
}

2. Call the service

SAP Adobe forms on Cloud offers a number of APIs. All of them are documented here. I used the API ‘/adsRender/pdf - Render a PDF Form’ to render the PDF form.



As you can see in the API document, before the API is called, the template and the data files must be encoded as the REST API expects them to be sent as encoded strings.

I used the following code to encode the files.
 /**
* This method is used to encode the input files
* @param fileName
* @return
* @throws IOException
*/
private String encodeFileToBase64Binary(final String fileName) throws IOException {

String path = this.getClass().getClassLoader().getResource("").getPath();
String fullPath = URLDecoder.decode(path, "UTF-8");

File file = new File(fullPath + fileName);
return Base64.getEncoder().encodeToString(FileUtils.readFileToByteArray(file));

}

Now we have everything we need to call the service. Here is the code to call the service. I have documented the code in-line for your understanding.
/**
* This method is used to call the Adobe service
* @return
*/
private String callService(){
//1. Get the oAuth token
String token = getoauthToken();

//2. Prepare the request headers
String url = "https://adsrestapiformsprocessing-sXXXXXXXtrial.hanatrial.ondemand.com/ads.restapi/v1/adsRender/pdf";
HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost request = new HttpPost(url);

request.addHeader("Authorization", "Bearer "+token);
request.addHeader("Content-Type", "application/json");

//3. Encode the form template file
String inputFileName = "adbforms\\Invoice.xdp";
String encxdp = "";

try {
encxdp = encodeFileToBase64Binary(inputFileName);
} catch (IOException e1) {
e1.printStackTrace();
}

//4. Encode the data xml file
inputFileName = "adbforms\\gendata.xml";
String encdata = "";
try {
encdata = encodeFileToBase64Binary(inputFileName);
} catch (IOException e1) {
e1.printStackTrace();
}

//5. Prepare the body of the request
String json = "{ "
+ "\"xdpTemplate\": \""+encxdp+"\", "
+ "\"xmlData\": \""+encdata+"\"}";

StringEntity input = null;
try
{
input = new StringEntity(json);
}catch(UnsupportedEncodingException e)
{
e.printStackTrace();
}
//6. Call the service and get the result
request.setEntity(input);
HttpResponse response = null;
try
{
response = httpClient.execute(request);
}catch(IOException e)
{
e.printStackTrace();
}

//7. Retrieve the file name and content from the response
String file = null;
String fileName = null;
try {
JSONObject tokenjson = new JSONObject(IOUtils.toString(response.getEntity().getContent(), "UTF-8"));
file = tokenjson.getString("fileContent");
fileName = tokenjson.getString("fileName");
//8. Decode and write the file.
writeUsingOutputStream(file, fileName);

} catch (IOException e) {
e.printStackTrace();
}catch(UnsupportedOperationException e)
{
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return file;
}

The rest API call gives the PDF file as an encoded string. I used the following piece of code to decode the string and write it as a PDF file.
/**
* This method is used to decode and the write the output PDF file.
* @param data
* @param fileName
*/
private void writeUsingOutputStream(String data, String fileName) {

fileName = "adbforms/test.pdf";
byte[] decoded = Base64.getDecoder().decode(data);
String path = this.getClass().getClassLoader().getResource("").getPath();
try {
String fullPath = URLDecoder.decode(path, "UTF-8");
File file = new File(fullPath + fileName);
FileUtils.writeByteArrayToFile(file, decoded);

} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

I ran the java application with the data XML file as shown below



Here is the output PDF response from the cloud service.



Ideally, in a real-world invoice application the xdp file will remain the same for all the calls as it holds the layout, but the data file (.xml) needs to be generated at runtime for each invoice before the Adobe API is called.

As always, please feel free to comment if you have any feedback or questions.
23 Comments
Labels in this area